idontwant2see.IDontWant2See.java Source code

Java tutorial

Introduction

Here is the source code for idontwant2see.IDontWant2See.java

Source

/*
 * IDontWant2See - Plugin for TV-Browser
 * Copyright (C) 2008 Ren Mach
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * SVN information:
 *     $Date$
 *   $Author$
 * $Revision$
 */
package idontwant2see;

import java.awt.AWTEvent;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.DataOutput;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;

import org.apache.commons.lang.StringUtils;

import util.ui.Localizer;
import util.ui.UiUtilities;
import util.ui.WindowClosingIf;

import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

import devplugin.ActionMenu;
import devplugin.ContextMenuAction;
import devplugin.Date;
import devplugin.ImportanceValue;
import devplugin.Plugin;
import devplugin.PluginInfo;
import devplugin.PluginsFilterComponent;
import devplugin.PluginsProgramFilter;
import devplugin.Program;
import devplugin.ProgramReceiveTarget;
import devplugin.SettingsTab;
import devplugin.Version;

/**
 * A very simple filter plugin to easily get rid of stupid programs in the
 * program table.
 *
 * @author Ren Mach
 */
public final class IDontWant2See extends Plugin implements AWTEventListener {

    private static final boolean PLUGIN_IS_STABLE = true;
    private static final Version PLUGIN_VERSION = new Version(0, 12, 1, PLUGIN_IS_STABLE);

    private static final String RECEIVE_TARGET_EXCLUDE_EXACT = "target_exclude_exact";

    static final Localizer mLocalizer = Localizer.getLocalizerFor(IDontWant2See.class);

    private static Date mCurrentDate = Date.getCurrentDate();
    private PluginsProgramFilter mFilter;
    private static IDontWant2See mInstance;
    private boolean mDateWasSet;
    private IDontWant2SeeSettings mSettings;

    private boolean mCtrlPressed;

    private HashMap<Program, Boolean> mMatchCache = new HashMap<Program, Boolean>(1000);

    private static final Pattern PATTERN_TITLE_PART = Pattern.compile("(?i)" // case insensitive matching
            + "(.*)" // ignore everything at the beginning
            + "((" // have two alternatives: optional and mandatory brackets
            + "\\(?(" // optional brackets begin
            + "(Teil \\d+)" + "|" + "(Teil \\d+/\\d+)" + "|" + "(Teil \\d+ von \\d+)" + "|" + "(Folge \\d+)" + "|"
            + "(Folge \\d+/\\d+)" + "|" + "(Folge \\d+ von \\d+)" + "|" + "(Best of)" + "|" + "(\\d+/\\d+)" + "|"
            + "(\\sI)" + "|" // 1
            + "(\\sII)" + "|" // 2
            + "(\\sIII)" + "|" // 3
            + "(\\sIV)" + "|" // 4
            + "(\\sV)" // 5
            + ")\\)?" // optional brackets end
            + ")|(" // 2. alternative: mandatory brackets
            + "\\((" // mandatory brackets begin
            + "(Fortsetzung)" + "|" + "(\\d+)" + ")\\)" // mandatory brackets end
            + "))" + "$"); // at the end only

    public static Version getVersion() {
        return PLUGIN_VERSION;
    }

    /**
     * Creates an instance of this plugin.
     */
    public IDontWant2See() {
        mInstance = this;
        mSettings = new IDontWant2SeeSettings();
        mDateWasSet = false;
    }

    static IDontWant2See getInstance() {
        return mInstance;
    }

    public void handleTvDataUpdateFinished() {
        clearCache();
        setCurrentDate();
        mDateWasSet = false;

        for (IDontWant2SeeListEntry entry : mSettings.getSearchList()) {
            entry.resetDateWasSetFlag();
        }
    }

    void clearCache() {
        mMatchCache = new HashMap<Program, Boolean>(1000);
    }

    private static void setCurrentDate() {
        mCurrentDate = Date.getCurrentDate();
    }

    public ImportanceValue getImportanceValueForProgram(Program p) {
        if (!acceptInternal(p)) {
            return new ImportanceValue((byte) 1, mSettings.getProgramImportance());
        }

        return new ImportanceValue((byte) 1, Program.DEFAULT_PROGRAM_IMPORTANCE);
    }

    boolean acceptInternal(final Program program) {
        if (!mDateWasSet) {
            mSettings.setLastUsedDate(getCurrentDate());
            mDateWasSet = true;
        }
        final Boolean result = mMatchCache.get(program);
        if (result != null) {
            return result;
        }

        // calculate lower case title only once, not for each entry again
        final String title = program.getTitle();
        final String lowerCaseTitle = title.toLowerCase();
        for (IDontWant2SeeListEntry entry : mSettings.getSearchList()) {
            if (entry.matchesProgramTitle(title, lowerCaseTitle)) {
                return putCache(program, false);
            }
        }

        return putCache(program, true);
    }

    private boolean putCache(final Program program, final boolean matches) {
        mMatchCache.put(program, matches);
        return matches;
    }

    public PluginInfo getInfo() {
        return new PluginInfo(IDontWant2See.class, mLocalizer.msg("name", "I don't want to see!"),
                mLocalizer.msg("desc",
                        "Removes all programs with an entered search text in the title from the program table."),
                "Ren\u00e9 Mach", "GPL");
    }

    int getSearchTextIndexForProgram(final Program program) {
        if (program != null) {
            // calculate lower case title only once, not for each entry again
            final String title = program.getTitle();
            final String lowerCaseTitle = title.toLowerCase();
            for (int i = 0; i < mSettings.getSearchList().size(); i++) {
                if (mSettings.getSearchList().get(i).matchesProgramTitle(title, lowerCaseTitle)) {
                    return i;
                }
            }
        }

        return -1;
    }

    public ActionMenu getButtonAction() {
        final ContextMenuAction baseAction = new ContextMenuAction(mLocalizer.msg("name", "I don't want to see!"),
                createImageIcon("apps", "idontwant2see", 16));

        final ContextMenuAction openExclusionList = new ContextMenuAction(
                mLocalizer.msg("editExclusionList", "Edit exclusion list"),
                createImageIcon("apps", "idontwant2see", 16));
        openExclusionList.putValue(Plugin.BIG_ICON, createImageIcon("apps", "idontwant2see", 22));
        openExclusionList.setActionListener(new ActionListener() {
            public void actionPerformed(final ActionEvent e) {
                final Window w = UiUtilities.getLastModalChildOf(getParentFrame());

                JDialog temDlg = null;

                if (w instanceof JDialog) {
                    temDlg = new JDialog((JDialog) w, true);
                } else {
                    temDlg = new JDialog((JFrame) w, true);
                }

                final JDialog exclusionListDlg = temDlg;
                exclusionListDlg.setTitle(mLocalizer.msg("name", "I don't want to see!") + " - "
                        + mLocalizer.msg("editExclusionList", "Edit exclusion list"));

                UiUtilities.registerForClosing(new WindowClosingIf() {
                    public void close() {
                        exclusionListDlg.dispose();
                    }

                    public JRootPane getRootPane() {
                        return exclusionListDlg.getRootPane();
                    }
                });

                final ExclusionTablePanel exclusionPanel = new ExclusionTablePanel(mSettings);

                final JButton ok = new JButton(Localizer.getLocalization(Localizer.I18N_OK));
                ok.addActionListener(new ActionListener() {
                    public void actionPerformed(final ActionEvent e) {
                        exclusionPanel.saveSettings(mSettings);
                        exclusionListDlg.dispose();
                    }
                });

                final JButton cancel = new JButton(Localizer.getLocalization(Localizer.I18N_CANCEL));
                cancel.addActionListener(new ActionListener() {
                    public void actionPerformed(final ActionEvent e) {
                        exclusionListDlg.dispose();
                    }
                });

                final FormLayout layout = new FormLayout("0dlu:grow,default,3dlu,default",
                        "fill:500px:grow,2dlu,default,5dlu,default");
                layout.setColumnGroups(new int[][] { { 2, 4 } });

                final CellConstraints cc = new CellConstraints();
                final PanelBuilder pb = new PanelBuilder(layout, (JPanel) exclusionListDlg.getContentPane());
                pb.setDefaultDialogBorder();

                pb.add(exclusionPanel, cc.xyw(1, 1, 4));
                pb.addSeparator("", cc.xyw(1, 3, 4));
                pb.add(ok, cc.xy(2, 5));
                pb.add(cancel, cc.xy(4, 5));

                layoutWindow("exclusionListDlg", exclusionListDlg, new Dimension(600, 450));
                exclusionListDlg.setVisible(true);
            }
        });

        final ContextMenuAction undo = new ContextMenuAction(
                mLocalizer.msg("undoLastExclusion", "Undo last exclusion"),
                createImageIcon("actions", "edit-undo", 16));
        undo.putValue(Plugin.BIG_ICON, createImageIcon("actions", "edit-undo", 22));
        undo.setActionListener(new ActionListener() {
            public void actionPerformed(final ActionEvent e) {
                String lastEnteredExclusionString = mSettings.getLastEnteredExclusionString();
                if (lastEnteredExclusionString.length() > 0) {
                    for (int i = mSettings.getSearchList().size() - 1; i >= 0; i--) {
                        if (mSettings.getSearchList().get(i).getSearchText().equals(lastEnteredExclusionString)) {
                            mSettings.getSearchList().remove(i);
                        }
                    }

                    mSettings.setLastEnteredExclusionString("");

                    updateFilter(true);
                }
            }
        });

        return new ActionMenu(baseAction, new Action[] { openExclusionList, undo });
    }

    public ActionMenu getContextMenuActions(final Program p) {
        if (p == null) {
            return null;
        }
        // check if this program is already hidden
        final int index = getSearchTextIndexForProgram(p);

        // return menu to hide the program
        if (index == -1 || p.equals(getPluginManager().getExampleProgram())) {
            AbstractAction actionDontWant = getActionDontWantToSee(p);

            if (mSettings.isSimpleMenu() && !mCtrlPressed) {
                final Matcher matcher = PATTERN_TITLE_PART.matcher(p.getTitle());
                if (matcher.matches()) {
                    actionDontWant = getActionInputTitle(p, matcher.group(2));
                }
                actionDontWant.putValue(Action.NAME, mLocalizer.msg("name", "I don't want to see!"));
                actionDontWant.putValue(Action.SMALL_ICON, createImageIcon("apps", "idontwant2see", 16));

                return new ActionMenu(actionDontWant);
            } else {
                final AbstractAction actionInput = getActionInputTitle(p, null);
                final ContextMenuAction baseAction = new ContextMenuAction(
                        mLocalizer.msg("name", "I don't want to see!"),
                        createImageIcon("apps", "idontwant2see", 16));

                return new ActionMenu(baseAction, new Action[] { actionDontWant, actionInput });
            }
        }

        // return menu to show the program
        return new ActionMenu(getActionShowAgain(p));
    }

    private ContextMenuAction getActionShowAgain(final Program p) {
        return new ContextMenuAction(mLocalizer.msg("menu.reshow", "I want to see!"),
                createImageIcon("actions", "edit-paste", 16)) {
            public void actionPerformed(final ActionEvent e) {
                mSettings.showAgain(p);
                updateFilter(!mSettings.isSwitchToMyFilter());
            }
        };
    }

    private AbstractAction getActionInputTitle(final Program p, final String part) {
        return new AbstractAction(mLocalizer.msg("menu.userEntered", "User entered value")) {
            public void actionPerformed(final ActionEvent e) {
                final JCheckBox caseSensitive = new JCheckBox(mLocalizer.msg("caseSensitive", "case sensitive"));
                String title = p.getTitle();
                ArrayList<String> items = new ArrayList<String>();
                if (!StringUtils.isEmpty(part)) {
                    String shortTitle = title.trim().substring(0, title.length() - part.length()).trim();
                    shortTitle = StringUtils.removeEnd(shortTitle, "-").trim();
                    shortTitle = StringUtils.removeEnd(shortTitle, "(").trim();
                    items.add(shortTitle + "*");
                }
                int index = title.indexOf(" - ");
                if (index > 0) {
                    items.add(title.substring(0, index).trim() + "*");
                }
                items.add(title);
                index = title.lastIndexOf(':');
                if (index > 0) {
                    items.add(title.substring(0, index).trim() + "*");
                }
                final JComboBox input = new JComboBox(items.toArray(new String[items.size()]));
                input.setEditable(true);

                input.addAncestorListener(new AncestorListener() {
                    public void ancestorAdded(final AncestorEvent event) {
                        event.getComponent().requestFocus();
                    }

                    public void ancestorMoved(final AncestorEvent event) {
                    }

                    public void ancestorRemoved(final AncestorEvent event) {
                    }
                });

                if (JOptionPane.showConfirmDialog(UiUtilities.getLastModalChildOf(getParentFrame()),
                        new Object[] {
                                mLocalizer.msg("exclusionText",
                                        "What should be excluded? (You can use the wildcard *)"),
                                input, caseSensitive },
                        mLocalizer.msg("exclusionTitle", "Exclusion value entering"),
                        JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) {
                    String test = "";

                    String result = (String) input.getSelectedItem();
                    if (result != null) {
                        test = result.replaceAll("\\*+", "\\*").trim();

                        if (test.length() >= 0 && !test.equals("*")) {
                            mSettings.getSearchList()
                                    .add(new IDontWant2SeeListEntry(result, caseSensitive.isSelected()));
                            mSettings.setLastEnteredExclusionString(result);
                            updateFilter(!mSettings.isSwitchToMyFilter());
                        }
                    }

                    if (test.trim().length() <= 1) {
                        JOptionPane.showMessageDialog(UiUtilities.getLastModalChildOf(getParentFrame()),
                                mLocalizer.msg("notValid", "The entered text is not valid."),
                                Localizer.getLocalization(Localizer.I18N_ERROR), JOptionPane.ERROR_MESSAGE);
                    }
                }
            }
        };
    }

    private AbstractAction getActionDontWantToSee(final Program p) {
        return new AbstractAction(mLocalizer.msg("menu.completeCaseSensitive", "Complete title case-sensitive")) {
            public void actionPerformed(final ActionEvent e) {
                mSettings.getSearchList().add(new IDontWant2SeeListEntry(p.getTitle(), true));
                mSettings.setLastEnteredExclusionString(p.getTitle());
                updateFilter(!mSettings.isSwitchToMyFilter());
            }
        };
    }

    void updateFilter(final boolean update) {
        clearCache();

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                if (!update) {
                    getPluginManager().getFilterManager().setCurrentFilter(getFilter());
                } else {
                    getPluginManager().getFilterManager()
                            .setCurrentFilter(getPluginManager().getFilterManager().getCurrentFilter());
                }
            }
        });

        saveMe();
    }

    public PluginsProgramFilter[] getAvailableFilter() {
        return new PluginsProgramFilter[] { getFilter() };
    }

    private PluginsProgramFilter getFilter() {
        if (mFilter == null) {
            mFilter = new PluginsProgramFilter(mInstance) {
                public String getSubName() {
                    return "";
                }

                public boolean accept(final Program prog) {
                    return acceptInternal(prog);
                }
            };
        }
        return mFilter;
    }

    public void readData(final ObjectInputStream in) throws IOException, ClassNotFoundException {
        final int version = in.readInt(); // read Version

        int n = in.readInt();

        if (version <= 2) {
            for (int i = 0; i < n; i++) {
                final StringBuilder value = new StringBuilder("*");
                value.append(in.readUTF()).append("*");

                mSettings.getSearchList().add(new IDontWant2SeeListEntry(value.toString(), false));
            }

            if (version == 2) {
                n = in.readInt();

                for (int i = 0; i < n; i++) {
                    mSettings.getSearchList().add(new IDontWant2SeeListEntry(in.readUTF(), true));
                }

                mSettings.setSimpleMenu(false);
            }
        } else {
            for (int i = 0; i < n; i++) {
                mSettings.getSearchList().add(new IDontWant2SeeListEntry(in, version));
            }

            mSettings.setSimpleMenu(in.readBoolean());

            if (version >= 4) {
                mSettings.setSwitchToMyFilter(in.readBoolean());
            }
            if (version >= 5) {
                mSettings.setLastEnteredExclusionString(in.readUTF());
            }
            if (version >= 6) {
                mSettings.setLastUsedDate(Date.readData(in));
            }
            if (version >= 7) {
                mSettings.setProgramImportance(in.readByte());
            } else {
                mSettings.setProgramImportance(Program.DEFAULT_PROGRAM_IMPORTANCE);
            }
        }
    }

    public void writeData(final ObjectOutputStream out) throws IOException {
        // sort the list so that often used entries are in the front (for faster lookup)
        Collections.sort(mSettings.getSearchList(), new Comparator<IDontWant2SeeListEntry>() {

            public int compare(IDontWant2SeeListEntry o1, IDontWant2SeeListEntry o2) {
                return o2.getLastMatchedDate().compareTo(o1.getLastMatchedDate());
            }
        });

        out.writeInt(7); //version
        out.writeInt(mSettings.getSearchList().size());

        for (IDontWant2SeeListEntry entry : mSettings.getSearchList()) {
            entry.writeData(out);
        }

        out.writeBoolean(mSettings.isSimpleMenu());
        out.writeBoolean(mSettings.isSwitchToMyFilter());

        out.writeUTF(mSettings.getLastEnteredExclusionString());

        mSettings.getLastUsedDate().writeData((DataOutput) out);

        out.writeByte(mSettings.getProgramImportance());
    }

    public SettingsTab getSettingsTab() {
        return new IDontWant2SeeSettingsTab(mSettings);
    }

    static Date getCurrentDate() {
        return mCurrentDate;
    }

    @SuppressWarnings("unchecked")
    public Class<? extends PluginsFilterComponent>[] getAvailableFilterComponentClasses() {
        return (Class<? extends PluginsFilterComponent>[]) new Class[] { IDontWant2SeeFilterComponent.class };
    }

    @Override
    public boolean canReceiveProgramsWithTarget() {
        return true;
    }

    @Override
    public ProgramReceiveTarget[] getProgramReceiveTargets() {
        return new ProgramReceiveTarget[] { new ProgramReceiveTarget(this,
                mLocalizer.msg("programTarget", "Exclude programs"), RECEIVE_TARGET_EXCLUDE_EXACT) };
    }

    @Override
    public boolean receivePrograms(final Program[] programArr, final ProgramReceiveTarget receiveTarget) {
        if (receiveTarget.getTargetId().equals(RECEIVE_TARGET_EXCLUDE_EXACT)) {
            if (programArr.length > 0) {
                for (Program program : programArr) {
                    if (getSearchTextIndexForProgram(program) == -1) {
                        mSettings.getSearchList().add(new IDontWant2SeeListEntry(program.getTitle(), true));
                        mSettings.setLastEnteredExclusionString(program.getTitle());
                    }
                }
                updateFilter(!mSettings.isSwitchToMyFilter());
            }
            return true;
        }
        return false;
    }

    @Override
    public void onActivation() {
        mFilter = new PluginsProgramFilter(this) {
            public String getSubName() {
                return "";
            }

            public boolean accept(final Program prog) {
                return acceptInternal(prog);
            }
        };

        Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK);
    }

    @Override
    public void onDeactivation() {
        Toolkit.getDefaultToolkit().removeAWTEventListener(this);
    }

    public void eventDispatched(final AWTEvent event) {
        if (event instanceof KeyEvent) {
            final KeyEvent keyEvent = (KeyEvent) event;
            mCtrlPressed = keyEvent.isControlDown();
            // System.out.println("Ctrl " + mCtrlPressed);
        }
    }

    public String getPluginCategory() {
        //Plugin.OTHER_CATEGORY
        return "misc";
    }

}