/*
* Copyright 2010-2015 Institut Pasteur.
*
* This file is part of Icy.
*
* Icy 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 3 of the License, or
* (at your option) any later version.
*
* Icy 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 Icy. If not, see <http://www.gnu.org/licenses/>.
*/
package icy.gui.menu;
import icy.action.FileActions;
import icy.action.GeneralActions;
import icy.action.PreferencesActions;
import icy.file.FileUtil;
import icy.file.Importer;
import icy.file.Loader;
import icy.gui.component.button.IcyCommandButton;
import icy.gui.component.menu.IcyRibbonApplicationMenuEntryPrimary;
import icy.gui.component.menu.IcyRibbonApplicationMenuEntrySecondary;
import icy.gui.plugin.PluginCommandButton;
import icy.gui.util.ComponentUtil;
import icy.main.Icy;
import icy.plugin.PluginDescriptor;
import icy.plugin.PluginLauncher;
import icy.plugin.PluginLoader;
import icy.plugin.PluginLoader.PluginLoaderEvent;
import icy.plugin.PluginLoader.PluginLoaderListener;
import icy.preferences.GeneralPreferences;
import icy.preferences.IcyPreferences;
import icy.resource.ResourceUtil;
import icy.resource.icon.IcyIcon;
import icy.sequence.Sequence;
import icy.sequence.SequenceImporter;
import icy.system.IcyExceptionHandler;
import icy.system.thread.ThreadUtil;
import icy.type.collection.CollectionUtil;
import icy.type.collection.list.RecentFileList;
import icy.util.StringUtil;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.List;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingConstants;
import org.pushingpixels.flamingo.api.common.AbstractCommandButton;
import org.pushingpixels.flamingo.api.common.CommandButtonDisplayState;
import org.pushingpixels.flamingo.api.common.JCommandButton;
import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonKind;
import org.pushingpixels.flamingo.api.common.JCommandButtonPanel;
import org.pushingpixels.flamingo.api.common.JCommandButtonPanel.LayoutKind;
import org.pushingpixels.flamingo.api.common.RichTooltip;
import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenu;
import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntryPrimary;
import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntrySecondary;
/**
* @author Stephane
*/
public class ApplicationMenu extends RibbonApplicationMenu implements PluginLoaderListener
{
static final int RECENTFILE_MAXLEN = 100;
/**
* Secondary panel management for "Open Recent File"
*/
private class OpenRecentFileRollOverCallBack implements RibbonApplicationMenuEntryPrimary.PrimaryRolloverCallback
{
public OpenRecentFileRollOverCallBack()
{
super();
}
@Override
public void menuEntryActivated(JPanel targetPanel)
{
ComponentUtil.setPreferredWidth(targetPanel, 480);
final JCommandButtonPanel recentFilesPanel = new JCommandButtonPanel(CommandButtonDisplayState.MEDIUM);
// set to 1 column maximum
recentFilesPanel.setMaxButtonColumns(1);
recentFilesPanel.setLayoutKind(LayoutKind.ROW_FILL);
// recent files group
recentFilesPanel.addButtonGroup("Recent Files");
// remove obsolete entries
recentFileList.clean();
final int nbRecentFile = recentFileList.getSize();
final IcyIcon icon = new IcyIcon("document");
for (int i = 0; i < nbRecentFile; i++)
{
final String entry = recentFileList.getEntryAsName(i, RECENTFILE_MAXLEN, true);
if (!StringUtil.isEmpty(entry))
{
final JCommandButton button = new JCommandButton(entry, icon);
button.setHorizontalAlignment(SwingConstants.LEFT);
final String[] paths = recentFileList.getEntry(i);
// final File[] files = recentFileList.getEntryAsFiles(i);
final RichTooltip toolTip;
final int numFile = paths.length;
if (numFile == 1)
toolTip = new RichTooltip("Single file sequence", FileUtil.getFileName(paths[0]));
else
toolTip = new RichTooltip("Multiple files sequence", FileUtil.getFileName(paths[0]));
for (int j = 1; j < Math.min(10, numFile); j++)
toolTip.addDescriptionSection(FileUtil.getFileName(paths[j]));
if (numFile > 10)
{
toolTip.addDescriptionSection("...");
toolTip.addDescriptionSection(FileUtil.getFileName(paths[numFile - 1]));
}
button.setActionRichTooltip(toolTip);
button.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent ae)
{
Loader.load(CollectionUtil.asList(paths), false, true, true);
}
});
// add button to history panel
recentFilesPanel.addButtonToLastGroup(button);
}
}
// action group
recentFilesPanel.addButtonGroup("Action");
final IcyCommandButton clearButton = new IcyCommandButton(FileActions.clearRecentFilesAction);
recentFilesPanel.addButtonToLastGroup(clearButton);
targetPanel.removeAll();
targetPanel.setLayout(new BorderLayout());
targetPanel.add(new JScrollPane(recentFilesPanel), BorderLayout.CENTER);
targetPanel.validate();
}
}
/**
* Secondary panel management for "Import"
* Display all other importers (anything which is not "openable" from File)
*/
private class ImportResourceRollOverCallBack implements RibbonApplicationMenuEntryPrimary.PrimaryRolloverCallback
{
public ImportResourceRollOverCallBack()
{
super();
}
@Override
public void menuEntryActivated(JPanel targetPanel)
{
ComponentUtil.setPreferredWidth(targetPanel, 480);
final JCommandButtonPanel importPanel = new JCommandButtonPanel(CommandButtonDisplayState.MEDIUM);
// set to 1 column maximum
importPanel.setMaxButtonColumns(1);
importPanel.setLayoutKind(LayoutKind.ROW_FILL);
// add Sequence importers
final List<PluginDescriptor> sequenceImporterPlugins = PluginLoader.getPlugins(SequenceImporter.class);
if (!sequenceImporterPlugins.isEmpty())
{
importPanel.addButtonGroup("Sequence importer");
for (PluginDescriptor plugin : sequenceImporterPlugins)
{
final AbstractCommandButton button = PluginCommandButton.createButton(plugin, false, false, false);
button.setHorizontalAlignment(SwingConstants.LEFT);
button.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent event)
{
final AbstractCommandButton button = (AbstractCommandButton) event.getSource();
final PluginDescriptor pluginDescriptor = PluginLoader.getPlugin(button.getName());
if (pluginDescriptor != null)
{
try
{
// we really need to start the plugin here in case it uses EzPlug for instance
final SequenceImporter importer = (SequenceImporter) PluginLauncher
.startSafe(pluginDescriptor);
// asynchronous loading
ThreadUtil.bgRun(new Runnable()
{
@Override
public void run()
{
try
{
// plugin correctly started ? --> do load operation
final Sequence result = importer.load();
// display result sequence
if (result != null)
Icy.getMainInterface().addSequence(result);
}
catch (Exception exc)
{
IcyExceptionHandler.handleException(exc, false);
}
}
});
}
catch (Exception exc)
{
IcyExceptionHandler.handleException(exc, false);
}
}
}
});
importPanel.addButtonToLastGroup(button);
}
}
// add Sequence importers
final List<PluginDescriptor> importerPlugins = PluginLoader.getPlugins(Importer.class);
if (!importerPlugins.isEmpty())
{
importPanel.addButtonGroup("General importer");
for (PluginDescriptor plugin : importerPlugins)
{
final AbstractCommandButton button = PluginCommandButton.createButton(plugin, false, false, false);
button.setHorizontalAlignment(SwingConstants.LEFT);
button.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent event)
{
final AbstractCommandButton button = (AbstractCommandButton) event.getSource();
final PluginDescriptor pluginDescriptor = PluginLoader.getPlugin(button.getName());
if (pluginDescriptor != null)
{
try
{
// we really need to start the plugin here in case it uses EzPlug for instance
final Importer importer = (Importer) PluginLauncher.startSafe(pluginDescriptor);
// asynchronous loading
ThreadUtil.bgRun(new Runnable()
{
@Override
public void run()
{
try
{
// plugin correctly started ? --> do load operation
importer.load();
}
catch (Exception exc)
{
IcyExceptionHandler.handleException(exc, false);
}
}
});
}
catch (Exception exc)
{
IcyExceptionHandler.handleException(exc, false);
}
}
}
});
importPanel.addButtonToLastGroup(button);
}
}
targetPanel.removeAll();
targetPanel.setLayout(new BorderLayout());
targetPanel.add(new JScrollPane(importPanel), BorderLayout.CENTER);
targetPanel.validate();
}
}
/**
* Default roll callback
*/
private class EmptyRollOverCallBack implements RibbonApplicationMenuEntryPrimary.PrimaryRolloverCallback
{
public EmptyRollOverCallBack()
{
super();
}
@Override
public void menuEntryActivated(JPanel targetPanel)
{
ComponentUtil.setPreferredWidth(targetPanel, 480);
// ribbon need at least one component in targetPanel
targetPanel.removeAll();
targetPanel.add(new JPanel());
targetPanel.validate();
}
}
final RecentFileList recentFileList;
private final RibbonApplicationMenuEntryPrimary amepCreate;
private final RibbonApplicationMenuEntrySecondary amesCreateGraySequence;
private final RibbonApplicationMenuEntrySecondary amesCreateRGBSequence;
private final RibbonApplicationMenuEntrySecondary amesCreateRGBASequence;
private final RibbonApplicationMenuEntryPrimary amepOpen;
private final RibbonApplicationMenuEntryPrimary amepImport;
private final RibbonApplicationMenuEntryPrimary amepSaveDefault;
private final RibbonApplicationMenuEntrySecondary amepSave;
private final RibbonApplicationMenuEntrySecondary amepSaveAs;
private final RibbonApplicationMenuEntrySecondary amepSaveMetaData;
private final RibbonApplicationMenuEntryPrimary amepClose;
private final RibbonApplicationMenuEntrySecondary amesCloseCurrent;
private final RibbonApplicationMenuEntrySecondary amesCloseOthers;
private final RibbonApplicationMenuEntrySecondary amesCloseAll;
private final RibbonApplicationMenuEntryPrimary amepPreferences;
private final RibbonApplicationMenuEntryPrimary amepExit;
/**
*
*/
public ApplicationMenu()
{
super();
recentFileList = new RecentFileList(IcyPreferences.applicationRoot().node("loader"));
// CREATE SEQUENCE
amepCreate = new IcyRibbonApplicationMenuEntryPrimary(FileActions.newSequenceAction);
amesCreateGraySequence = new IcyRibbonApplicationMenuEntrySecondary(FileActions.newGraySequenceAction);
amesCreateRGBSequence = new IcyRibbonApplicationMenuEntrySecondary(FileActions.newRGBSequenceAction);
amesCreateRGBASequence = new IcyRibbonApplicationMenuEntrySecondary(FileActions.newARGBSequenceAction);
amepCreate.addSecondaryMenuGroup("New image", amesCreateGraySequence, amesCreateRGBSequence, amesCreateRGBASequence);
// OPEN & IMPORT
amepOpen = new IcyRibbonApplicationMenuEntryPrimary(FileActions.openSequenceAction);
amepOpen.setRolloverCallback(new OpenRecentFileRollOverCallBack());
amepImport = new IcyRibbonApplicationMenuEntryPrimary(new IcyIcon(ResourceUtil.ICON_DOC_IMPORT), "Import",
null, CommandButtonKind.POPUP_ONLY);
amepImport.setRolloverCallback(new ImportResourceRollOverCallBack());
// SAVE & EXPORT
amepSaveDefault = new IcyRibbonApplicationMenuEntryPrimary(FileActions.saveDefaultSequenceAction);
amepSave = new IcyRibbonApplicationMenuEntrySecondary(FileActions.saveSequenceAction);
amepSaveAs = new IcyRibbonApplicationMenuEntrySecondary(FileActions.saveAsSequenceAction);
amepSaveMetaData = new IcyRibbonApplicationMenuEntrySecondary(FileActions.saveMetaDataAction);
amepSaveDefault.addSecondaryMenuGroup("Save", amepSave, amepSaveAs, amepSaveMetaData);
// final RibbonApplicationMenuEntryPrimary amepExport = new
// RibbonApplicationMenuEntryPrimary(
// new ICYResizableIcon.Icy("doc_export.png"), "Export", new ActionListener()
// {
// @Override
// public void actionPerformed(ActionEvent e)
// {
// System.out.println("export action...");
// }
// }, CommandButtonKind.ACTION_ONLY);
// amepExport.setRolloverCallback(new DefaultRollOverCallBack());
// CLOSE
amepClose = new IcyRibbonApplicationMenuEntryPrimary(FileActions.closeSequenceAction);
amesCloseCurrent = new IcyRibbonApplicationMenuEntrySecondary(FileActions.closeCurrentSequenceAction);
amesCloseOthers = new IcyRibbonApplicationMenuEntrySecondary(FileActions.closeOthersSequencesAction);
amesCloseAll = new IcyRibbonApplicationMenuEntrySecondary(FileActions.closeAllSequencesAction);
amepClose.addSecondaryMenuGroup("Close Sequence", amesCloseCurrent, amesCloseOthers, amesCloseAll);
// PREFERENCES
amepPreferences = new IcyRibbonApplicationMenuEntryPrimary(PreferencesActions.preferencesAction);
// EXIT
amepExit = new IcyRibbonApplicationMenuEntryPrimary(GeneralActions.exitApplicationAction);
amepExit.setRolloverCallback(new EmptyRollOverCallBack());
// build menu
addMenuEntry(amepCreate);
addMenuEntry(amepOpen);
addMenuEntry(amepSaveDefault);
addMenuSeparator();
addMenuEntry(amepImport);
// addMenuEntry(amepExport);
addMenuSeparator();
addMenuEntry(amepClose);
addMenuSeparator();
addMenuEntry(amepPreferences);
addMenuSeparator();
addMenuEntry(amepExit);
setDefaultCallback(new OpenRecentFileRollOverCallBack());
refreshState();
PluginLoader.addListener(this);
}
private void refreshState()
{
final Sequence focusedSequence = Icy.getMainInterface().getActiveSequence();
final boolean hasImporter = (!PluginLoader.getPlugins(SequenceImporter.class).isEmpty())
|| (!PluginLoader.getPlugins(Importer.class).isEmpty());
amepImport.setEnabled(hasImporter);
amepSaveDefault.setEnabled(focusedSequence != null);
amepSave.setEnabled((focusedSequence != null) && !StringUtil.isEmpty(focusedSequence.getFilename()));
amepSaveAs.setEnabled(focusedSequence != null);
amepSaveMetaData.setEnabled((focusedSequence != null) && !StringUtil.isEmpty(focusedSequence.getFilename())
&& GeneralPreferences.getSequencePersistence());
amepClose.setEnabled(Icy.getMainInterface().getSequences().size() > 0);
}
/**
* @return the recentFileList
*/
public RecentFileList getRecentFileList()
{
return recentFileList;
}
public void addRecentLoadedFile(List<File> files)
{
addRecentLoadedFile(files.toArray(new File[files.size()]));
}
public void addRecentLoadedFile(File[] files)
{
recentFileList.addEntry(files);
}
public void addRecentLoadedFile(File file)
{
addRecentLoadedFile(new File[] {file});
}
/**
* Add a list of recently opened files (String format)
*/
public void addRecentFile(List<String> paths)
{
addRecentFile(paths.toArray(new String[paths.size()]));
}
public void addRecentFile(String[] paths)
{
recentFileList.addEntry(paths);
}
public void addRecentFile(String path)
{
addRecentFile(new String[] {path});
}
public void onSequenceActivationChange()
{
refreshState();
}
@Override
public void pluginLoaderChanged(PluginLoaderEvent e)
{
// amepImport.addSecondaryMenuGroup("Import a sequence", getImportEntries());
refreshState();
}
}