/* JMeld is a visual diff and merge tool. Copyright (C) 2007 Kees Kuip This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.jmeld.ui; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; import com.jidesoft.swing.JideTabbedPane; import org.jdesktop.swingworker.*; import org.jdesktop.swingworker.SwingWorker; import org.jmeld.Version; import org.jmeld.settings.JMeldSettings; import org.jmeld.ui.action.ActionHandler; import org.jmeld.ui.action.Actions; import org.jmeld.ui.action.MeldAction; import org.jmeld.ui.bar.LineNumberBarDialog; import org.jmeld.ui.search.SearchBarDialog; import org.jmeld.ui.search.SearchCommand; import org.jmeld.ui.search.SearchHits; import org.jmeld.ui.settings.SettingsPanel; import org.jmeld.ui.util.*; import org.jmeld.util.ObjectUtil; import org.jmeld.util.StringUtil; import org.jmeld.util.conf.ConfigurationListenerIF; import org.jmeld.vc.VersionControlUtil; import javax.help.HelpSet; import javax.help.JHelpContentViewer; import javax.help.JHelpNavigator; import javax.help.NavigatorView; import javax.swing.*; import javax.swing.event.AncestorEvent; import javax.swing.event.AncestorListener; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.*; import java.awt.event.*; import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.List; public class JMeldPanel extends JPanel implements ConfigurationListenerIF { public final Actions actions; // Options (enable/disable before adding this component to its container) public final Option SHOW_TOOLBAR_OPTION; public final Option SHOW_STATUSBAR_OPTION; public final Option SHOW_TABBEDPANE_OPTION; public final Option SHOW_FILE_TOOLBAR_OPTION; public final Option SHOW_FILE_STATUSBAR_OPTION; public final Option STANDALONE_INSTALLKEY_OPTION; private ActionHandler actionHandler; private JideTabbedPane tabbedPane; private JPanel barContainer; private AbstractBarDialog currentBarDialog; private SearchBarDialog searchBarDialog; private JComponent toolBar; private boolean mergeMode; private boolean started; public JMeldPanel() { setFocusable(true); addAncestorListener(getAncestorListener()); SHOW_TOOLBAR_OPTION = new Option(this, true); SHOW_STATUSBAR_OPTION = new Option(this, true); SHOW_TABBEDPANE_OPTION = new Option(this, true); SHOW_FILE_TOOLBAR_OPTION = new Option(this, true); SHOW_FILE_STATUSBAR_OPTION = new Option(this, true); STANDALONE_INSTALLKEY_OPTION = new Option(this, false); actions = new Actions(); } public JideTabbedPane getTabbedPane() { return tabbedPane; } private void start() { if (started) { return; } started = true; tabbedPane = new JideTabbedPane(); getTabbedPane().setFocusable(false); getTabbedPane().setShowCloseButtonOnTab(true); getTabbedPane().setShowCloseButtonOnSelectedTab(true); if (!SHOW_TABBEDPANE_OPTION.isEnabled()) { getTabbedPane().setShowTabArea(false); } // Pin the tabshape because the defaults do not look good // on lookandfeels other than JGoodies Plastic. getTabbedPane().setTabShape(JideTabbedPane.SHAPE_OFFICE2003); // Watch out: initActions uses 'tabbedPane' so this statement should be // after the instantiation of tabbedPane. initActions(); if (SHOW_TABBEDPANE_OPTION.isEnabled()) { // Watch out: actionHandler gets initialized in 'initActions' so this // statement should be AFTER initActions(); getTabbedPane().setCloseAction(getAction(actions.EXIT)); } setLayout(new BorderLayout()); addToolBar(); add(getTabbedPane(), BorderLayout.CENTER); add(getBar(), BorderLayout.PAGE_END); getTabbedPane().getModel().addChangeListener(getChangeListener()); JMeldSettings.getInstance().addConfigurationListener(this); //setTransferHandler(getDragAndDropHandler()); } public void openComparison(List<String> fileNameList) { String fileName1; String fileName2; if (fileNameList.size() <= 0) { return; } /* Possibilities; 1. <fileName>, <fileName> 2. <fileName>, <fileName>, <fileName>, <fileName> 3. <directory>, <directory> 4. <directory>, <fileName>, <fileName> ... 5. <directory (version controlled)> ad 2: I always assume filepairs! for instance: <file1> <file2> <file3> <file4> will open 2 filediffs "file1-file2" and "file3-file4". ad 4: The fileNames are relative and are also available in the <directory>. So this enables you to do: jmeld ../branches/branch1 src/lala.java src/haha.java This results in 2 compares: 1. ../branches/branch1/src/lala.java with ./src/lala.java 2. ../branches/branch1/src/haha.java with ./src/haha.java */ if (fileNameList.size() > 1) { if (new File(fileNameList.get(0)).isDirectory()) { fileName1 = fileNameList.get(0); for (int i = 1; i < fileNameList.size(); i++) { fileName2 = fileNameList.get(i); openComparison(fileName1, fileName2); } } else { for (int i = 0; i < fileNameList.size(); i += 2) { fileName1 = fileNameList.get(i); if (i + 1 >= fileNameList.size()) { continue; } fileName2 = fileNameList.get(i + 1); openComparison(fileName1, fileName2); } } } else { openComparison(fileNameList.get(0), null); } } public void openComparison(String leftName, String rightName) { File leftFile; File rightFile; File file; if (!StringUtil.isEmpty(leftName) && !StringUtil.isEmpty(rightName)) { leftFile = new File(leftName); rightFile = new File(rightName); if (leftFile.isDirectory()) { if (rightFile.isDirectory()) { new DirectoryComparison(this, leftFile, rightFile, JMeldSettings .getInstance().getFilter().getFilter("default")).execute(); } else { FileComparison fileComparison = new FileComparison(this, new File(leftFile, rightName), rightFile); fileComparison.setOpenInBackground(false); fileComparison.execute(); } } else { FileComparison fileComparison = new FileComparison(this, leftFile, rightFile); fileComparison.setOpenInBackground(false); fileComparison.execute(); } } else { if (!StringUtil.isEmpty(leftName)) { file = new File(leftName); if (file.exists() && VersionControlUtil.isVersionControlled(file)) { VersionControlComparison versionControlComparison = new VersionControlComparison(this, file); versionControlComparison.execute(); } } } } public MeldAction getAction(Actions.Action action) { return getActionHandler().get(action); } public void addToolBar() { if (SHOW_TOOLBAR_OPTION.isEnabled()) { if (toolBar != null) { remove(toolBar); } toolBar = getToolBar(); add(toolBar, BorderLayout.PAGE_START); revalidate(); } } private JComponent getToolBar() { JButton button; JToolBar tb; ToolBarBuilder builder; tb = new JToolBar(); tb.setFloatable(false); tb.setRollover(true); builder = new ToolBarBuilder(tb); button = WidgetFactory.getToolBarButton(getAction(actions.NEW)); builder.addButton(button); button = WidgetFactory.getToolBarButton(getAction(actions.SAVE)); builder.addButton(button); builder.addSeparator(); button = WidgetFactory.getToolBarButton(getAction(actions.UNDO)); builder.addButton(button); button = WidgetFactory.getToolBarButton(getAction(actions.REDO)); builder.addButton(button); builder.addSpring(); button = WidgetFactory.getToolBarButton(getAction(actions.SETTINGS)); builder.addButton(button); button = WidgetFactory.getToolBarButton(getAction(actions.HELP)); builder.addButton(button); button = WidgetFactory.getToolBarButton(getAction(actions.ABOUT)); builder.addButton(button); return tb; } private JComponent getBar() { CellConstraints cc; cc = new CellConstraints(); barContainer = new JPanel(new FormLayout("0:grow", "pref, pref, pref")); barContainer.add(new JSeparator(), cc.xy(1, 2)); if (SHOW_STATUSBAR_OPTION.isEnabled()) { barContainer.add(StatusBar.getInstance(), cc.xy(1, 3)); } return barContainer; } private SearchBarDialog getSearchBarDialog() { if (searchBarDialog == null) { searchBarDialog = new SearchBarDialog(this); } return searchBarDialog; } public void initActions() { MeldAction action; actionHandler = new ActionHandler(); action = actionHandler.createAction(this, actions.NEW); action.setIcon("stock_new"); action.setToolTip("Merge 2 new files"); action = actionHandler.createAction(this, actions.SAVE); action.setIcon("stock_save"); action.setToolTip("Save the changed files"); if (!STANDALONE_INSTALLKEY_OPTION.isEnabled()) { installKey("ctrl S", action); } action = actionHandler.createAction(this, actions.UNDO); action.setIcon("stock_undo"); action.setToolTip("Undo the latest change"); installKey("control Z", action); installKey("control Y", action); action = actionHandler.createAction(this, actions.REDO); action.setIcon("stock_redo"); action.setToolTip("Redo the latest change"); installKey("control R", action); action = actionHandler.createAction(this, actions.LEFT); installKey("LEFT", action); installKey("alt LEFT", action); installKey("alt KP_LEFT", action); action = actionHandler.createAction(this, actions.RIGHT); installKey("RIGHT", action); installKey("alt RIGHT", action); installKey("alt KP_RIGHT", action); action = actionHandler.createAction(this, actions.UP); installKey("UP", action); installKey("alt UP", action); installKey("alt KP_UP", action); installKey("F7", action); action = actionHandler.createAction(this, actions.DOWN); installKey("DOWN", action); installKey("alt DOWN", action); installKey("alt KP_DOWN", action); installKey("F8", action); action = actionHandler.createAction(this, actions.ZOOM_PLUS); installKey("alt EQUALS", action); installKey("shift alt EQUALS", action); installKey("alt ADD", action); action = actionHandler.createAction(this, actions.ZOOM_MIN); installKey("alt MINUS", action); installKey("shift alt MINUS", action); installKey("alt SUBTRACT", action); action = actionHandler.createAction(this, actions.GOTO_SELECTED); installKey("alt ENTER", action); action = actionHandler.createAction(this, actions.GOTO_FIRST); installKey("alt HOME", action); action = actionHandler.createAction(this, actions.GOTO_LAST); installKey("alt END", action); action = actionHandler.createAction(this, actions.GOTO_LINE); installKey("ctrl L", action); action = actionHandler.createAction(this, actions.START_SEARCH); installKey("ctrl F", action); action = actionHandler.createAction(this, actions.NEXT_SEARCH); installKey("F3", action); installKey("ctrl G", action); action = actionHandler.createAction(this, actions.PREVIOUS_SEARCH); installKey("shift F3", action); action = actionHandler.createAction(this, actions.REFRESH); installKey("F5", action); action = actionHandler.createAction(this, actions.MERGEMODE); installKey("F9", action); if (!STANDALONE_INSTALLKEY_OPTION.isEnabled()) { action = actionHandler.createAction(this, actions.HELP); action.setIcon("stock_help-agent"); installKey("F1", action); action = actionHandler.createAction(this, actions.ABOUT); action.setIcon("stock_about"); action = actionHandler.createAction(this, actions.SETTINGS); action.setIcon("stock_preferences"); action.setToolTip("Settings"); action = actionHandler.createAction(this, actions.EXIT); installKey("ESCAPE", action); } } public ActionHandler getActionHandler() { return actionHandler; } public void checkActions() { if (actionHandler != null) { actionHandler.checkActions(); } } public void doNew(ActionEvent ae) { PanelDialog dialog; dialog = new PanelDialog(this); dialog.show(); //TODO Son todo SwingWorker<String, Object>; //Agregar una interfaz comĂșn if (dialog.getFunction() == PanelDialog.Function.FILE_COMPARISON) { FileComparison fileComparison = new FileComparison(this, new File(dialog.getLeftFileName()), new File(dialog .getRightFileName())); fileComparison.setOpenInBackground(false); fileComparison.execute(); } else if (dialog.getFunction() == PanelDialog.Function.DIRECTORY_COMPARISON) { new DirectoryComparison(this, new File(dialog.getLeftDirectoryName()), new File(dialog.getRightDirectoryName()), dialog .getFilter()).execute(); } else if (dialog.getFunction() == PanelDialog.Function.VERSION_CONTROL) { VersionControlComparison versionControlComparison = new VersionControlComparison(this, new File(dialog .getVersionControlDirectoryName())); versionControlComparison.execute(); } } public void doSave(ActionEvent ae) { getCurrentContentPanel().doSave(); } public boolean isSaveEnabled() { JMeldContentPanelIF panel; panel = getCurrentContentPanel(); if (panel == null) { return false; } return panel.isSaveEnabled(); } public void doUndo(ActionEvent ae) { getCurrentContentPanel().doUndo(); } public boolean isUndoEnabled() { JMeldContentPanelIF panel; panel = getCurrentContentPanel(); if (panel == null) { return false; } return panel.isUndoEnabled(); } public void doRedo(ActionEvent ae) { getCurrentContentPanel().doRedo(); } public boolean isRedoEnabled() { JMeldContentPanelIF panel; panel = getCurrentContentPanel(); if (panel == null) { return false; } return panel.isRedoEnabled(); } public void doLeft(ActionEvent ae) { boolean shift = false; int onmask = MouseEvent.SHIFT_DOWN_MASK | MouseEvent.BUTTON1_DOWN_MASK; int offmask = MouseEvent.CTRL_DOWN_MASK; if ((ae.getModifiers() & (onmask | offmask)) == onmask) { shift = true; } getCurrentContentPanel().doLeft(shift); repaint(); } public void doRight(ActionEvent ae) { boolean shift = false; int onmask = MouseEvent.SHIFT_DOWN_MASK | MouseEvent.BUTTON1_DOWN_MASK; int offmask = MouseEvent.CTRL_DOWN_MASK; if ((ae.getModifiers() & (onmask | offmask)) == onmask) { shift = true; } getCurrentContentPanel().doRight(shift); repaint(); } public void doUp(ActionEvent ae) { getCurrentContentPanel().doUp(); repaint(); } public void doDown(ActionEvent ae) { getCurrentContentPanel().doDown(); repaint(); } public void doZoomPlus(ActionEvent ae) { getCurrentContentPanel().doZoom(true); repaint(); } public void doZoomMin(ActionEvent ae) { getCurrentContentPanel().doZoom(false); repaint(); } public void doGoToSelected(ActionEvent ae) { getCurrentContentPanel().doGoToSelected(); repaint(); } public void doGoToFirst(ActionEvent ae) { getCurrentContentPanel().doGoToFirst(); repaint(); } public void doGoToLast(ActionEvent ae) { getCurrentContentPanel().doGoToLast(); repaint(); } public void doGoToLine(ActionEvent ae) { activateBarDialog(new LineNumberBarDialog(this)); } public void doGoToLine(int line) { getCurrentContentPanel().doGoToLine(line); repaint(); deactivateBarDialog(); } public void doStartSearch(ActionEvent ae) { SearchBarDialog sbd; sbd = getSearchBarDialog(); sbd.setSearchText(getSelectedSearchText()); activateBarDialog(sbd); } public void doStopSearch(ActionEvent ae) { deactivateBarDialog(); for (JMeldContentPanelIF cp : getContentPanelList(getTabbedPane())) { cp.doStopSearch(); } } public SearchHits doSearch(ActionEvent ae) { return getCurrentContentPanel().doSearch(); } SearchCommand getSearchCommand() { if (currentBarDialog != getSearchBarDialog()) { return null; } return getSearchBarDialog().getCommand(); } public void doNextSearch(ActionEvent ae) { if (currentBarDialog != getSearchBarDialog()) { return; } getCurrentContentPanel().doNextSearch(); } public void doPreviousSearch(ActionEvent ae) { if (currentBarDialog != getSearchBarDialog()) { return; } getCurrentContentPanel().doPreviousSearch(); } private String getSelectedSearchText() { return getCurrentContentPanel().getSelectedText(); } public void doRefresh(ActionEvent ae) { getCurrentContentPanel().doRefresh(); } public void doMergeMode(ActionEvent ae) { MeldAction action; mergeMode = !mergeMode; action = getAction(actions.LEFT); installKey(mergeMode, "LEFT", action); action = getAction(actions.RIGHT); installKey(mergeMode, "RIGHT", action); action = getAction(actions.UP); installKey(mergeMode, "UP", action); action = getAction(actions.DOWN); installKey(mergeMode, "DOWN", action); getCurrentContentPanel().doMergeMode(mergeMode); requestFocus(); if (mergeMode) { StatusBar.getInstance() .setNotification(actions.MERGEMODE.getName(), ImageUtil.getSmallImageIcon("jmeld_mergemode-on")); } else { StatusBar.getInstance().removeNotification(actions.MERGEMODE.getName()); } } public void doHelp(ActionEvent ae) { try { JPanel panel; AbstractContentPanel content; URL url; HelpSet helpSet; JHelpContentViewer viewer; JHelpNavigator navigator; NavigatorView navigatorView; JSplitPane splitPane; String contentId; contentId = "HelpPanel"; if (checkAlreadyOpen(contentId)) { return; } url = HelpSet.findHelpSet(getClass().getClassLoader(), "jmeld"); helpSet = new HelpSet(getClass().getClassLoader(), url); viewer = new JHelpContentViewer(helpSet); navigatorView = helpSet.getNavigatorView("TOC"); navigator = (JHelpNavigator) navigatorView.createNavigator(viewer .getModel()); splitPane = new JSplitPane(); splitPane.setLeftComponent(navigator); splitPane.setRightComponent(viewer); content = new AbstractContentPanel(); content.setId(contentId); content.setLayout(new BorderLayout()); content.add(splitPane, BorderLayout.CENTER); /* content = new HelpPanel(this); */ getTabbedPane().addTab("Help", ImageUtil.getSmallImageIcon("stock_help-agent"), content); getTabbedPane().setSelectedComponent(content); } catch (Exception ex) { ex.printStackTrace(); } } public void doAbout(ActionEvent ae) { AbstractContentPanel content; String contentId; contentId = "AboutPanel"; if (checkAlreadyOpen(contentId)) { return; } content = new AbstractContentPanel(); content.setId(contentId); content.setLayout(new BorderLayout()); content.add(new JButton("JMeld version: " + Version.getVersion()), BorderLayout.CENTER); getTabbedPane().addTab("About", ImageUtil.getSmallImageIcon("stock_about"), content); getTabbedPane().setSelectedComponent(content); } public void doExit(ActionEvent ae) { JMeldContentPanelIF cp; // Stop the searchBarDialog if it is showing. if (currentBarDialog == getSearchBarDialog()) { doStopSearch(ae); return; } if (currentBarDialog != null) { deactivateBarDialog(); return; } cp = getCurrentContentPanel(); if (cp == null) { return; } // Detect if this close is due to pressing ESC. if (ae.getSource() == this) { if (!cp.checkExit()) { return; } } // Exit a tab! doExitTab((Component) getCurrentContentPanel()); } public void doSettings(ActionEvent ae) { AbstractContentPanel content; String contentId; contentId = "SettingsPanel"; if (checkAlreadyOpen(contentId)) { return; } content = new SettingsPanel(this); content.setId(contentId); getTabbedPane().addTab("Settings", ImageUtil .getSmallImageIcon("stock_preferences"), content); getTabbedPane().setSelectedComponent(content); } private boolean checkAlreadyOpen(String contentId) { AbstractContentPanel contentPanel; contentPanel = getAlreadyOpen(getTabbedPane(), contentId); if (contentPanel != null) { getTabbedPane().setSelectedComponent(contentPanel); return true; } return false; } public static AbstractContentPanel getAlreadyOpen(JideTabbedPane tabbedPane, String contentId) { for (AbstractContentPanel contentPanel : getContentPanelList(tabbedPane)) { if (ObjectUtil.equals(contentPanel.getId(), contentId)) { System.out.println("already open: " + contentId); return contentPanel; } } return null; } private ChangeListener getChangeListener() { return new ChangeListener() { public void stateChanged(ChangeEvent e) { checkActions(); } }; } public WindowListener getWindowListener() { return new WindowAdapter() { @Override public void windowClosing(WindowEvent we) { for (JMeldContentPanelIF contentPanel : getContentPanelList(getTabbedPane())) { if (!contentPanel.checkSave()) { return; } } System.exit(1); } }; } private AbstractContentPanel getCurrentContentPanel() { return (AbstractContentPanel) getTabbedPane().getSelectedComponent(); } public static List<AbstractContentPanel> getContentPanelList(JideTabbedPane tabbedPane) { List<AbstractContentPanel> result; result = new ArrayList<AbstractContentPanel>(); if (tabbedPane != null) { for (int i = 0; i < tabbedPane.getTabCount(); i++) { result.add((AbstractContentPanel) tabbedPane.getComponentAt(i)); } } return result; } private void installKey(boolean enabled, String key, MeldAction action) { if (!enabled) { deInstallKey(key, action); } else { installKey(key, action); } } private void installKey(String key, MeldAction action) { SwingUtil.installKey(this, key, action); } private void deInstallKey(String key, MeldAction action) { SwingUtil.deInstallKey(this, key, action); } /* private TabIcon getTabIcon( String iconName, String text) { TabIcon icon; icon = new TabIcon( ImageUtil.getSmallImageIcon(iconName), text); icon.addExitListener(getTabExitListener()); return icon; } private TabExitListenerIF getTabExitListener() { return new TabExitListenerIF() { public boolean doExit(TabExitEvent te) { int tabIndex; tabIndex = te.getTabIndex(); if (tabIndex == -1) { return false; } return doExitTab(tabbedPane.getComponentAt(tabIndex)); } }; } */ private boolean doExitTab(Component component) { AbstractContentPanel content; Icon icon; int index; if (component == null) { return false; } index = getTabbedPane().indexOfComponent(component); if (index == -1) { return false; } if (component instanceof AbstractContentPanel) { content = (AbstractContentPanel) component; if (!content.checkSave()) { return false; } } icon = getTabbedPane().getIconAt(index); if (icon != null && icon instanceof TabIcon) { ((TabIcon) icon).exit(); } getTabbedPane().remove(component); return true; } public void activateBarDialog(AbstractBarDialog bar) { CellConstraints cc; deactivateBarDialog(); cc = new CellConstraints(); barContainer.add(bar, cc.xy(1, 1)); bar.activate(); currentBarDialog = bar; barContainer.revalidate(); } public void deactivateBarDialog() { if (currentBarDialog != null) { barContainer.remove(currentBarDialog); barContainer.revalidate(); currentBarDialog.deactivate(); currentBarDialog = null; } } public void configurationChanged() { checkActions(); } /* == JDK6! private TransferHandler getDragAndDropHandler() { return new TransferHandler() { public boolean canImport(TransferHandler.TransferSupport support) { System.out.println(support); return false; } public boolean importData(TransferHandler.TransferSupport support) { return true; } }; } */ public boolean isStarted() { return started; } private AncestorListener getAncestorListener() { return new AncestorListener() { public void ancestorAdded(AncestorEvent event) { start(); } public void ancestorMoved(AncestorEvent event) { } public void ancestorRemoved(AncestorEvent event) { } }; } }