/*
* Copyright (C) 2010-2016 JPEXS
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.gui;
import com.jpexs.decompiler.flash.configuration.Configuration;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;
import org.pushingpixels.flamingo.api.common.AbstractCommandButton;
import org.pushingpixels.flamingo.api.common.CommandButtonDisplayState;
import org.pushingpixels.flamingo.api.common.CommandToggleButtonGroup;
import org.pushingpixels.flamingo.api.common.JCommandButton;
import org.pushingpixels.flamingo.api.common.JCommandButtonPanel;
import org.pushingpixels.flamingo.api.common.JCommandToggleButton;
import org.pushingpixels.flamingo.api.common.popup.JPopupPanel;
import org.pushingpixels.flamingo.api.common.popup.PopupPanelCallback;
import org.pushingpixels.flamingo.api.ribbon.AbstractRibbonBand;
import org.pushingpixels.flamingo.api.ribbon.JRibbon;
import org.pushingpixels.flamingo.api.ribbon.JRibbonBand;
import org.pushingpixels.flamingo.api.ribbon.JRibbonComponent;
import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenu;
import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntryFooter;
import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntryPrimary;
import org.pushingpixels.flamingo.api.ribbon.RibbonContextualTaskGroup;
import org.pushingpixels.flamingo.api.ribbon.RibbonElementPriority;
import org.pushingpixels.flamingo.api.ribbon.RibbonTask;
import org.pushingpixels.flamingo.api.ribbon.resize.BaseRibbonBandResizePolicy;
import org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizePolicies;
import org.pushingpixels.flamingo.api.ribbon.resize.IconRibbonBandResizePolicy;
import org.pushingpixels.flamingo.api.ribbon.resize.RibbonBandResizePolicy;
import org.pushingpixels.flamingo.internal.ui.ribbon.AbstractBandControlPanel;
/**
*
* @author JPEXS
*/
public class MainFrameRibbonMenu extends MainFrameMenu {
private final JRibbon ribbon;
private final Map<String, Object> menuItems = new HashMap<>();
private final Map<String, String> menuTitles = new HashMap<>();
private final Map<String, Boolean> menuOptional = new HashMap<>();
private final Map<String, String> menuIcons = new HashMap<>();
private final Map<String, ActionListener> menuLoaders = new HashMap<>();
private final Map<String, Integer> menuPriorities = new HashMap<>();
private final Map<String, List<String>> menuSubs = new HashMap<>();
private final Map<String, Integer> menuType = new HashMap<>();
private final Map<String, String> menuGroup = new HashMap<>();
private final Map<String, CommandToggleButtonGroup> menuToggleGroups = new HashMap<>();
private final Map<String, CommandToggleButtonGroup> menuToToggleGroup = new HashMap<>();
private static final int TYPE_MENU = 1;
private static final int TYPE_MENUITEM = 2;
private static final int TYPE_TOGGLEMENUITEM = 3;
private final Map<String, RibbonContextualTaskGroup> optionalGroups = new HashMap<>();
public MainFrameRibbonMenu(MainFrameRibbon mainFrame, JRibbon ribbon, boolean externalFlashPlayerUnavailable) {
super(mainFrame, externalFlashPlayerUnavailable);
this.ribbon = ribbon;
}
private String fixCommandTitle(String title) {
if (title.length() > 2) {
if (title.charAt(1) == ' ') {
title = title.charAt(0) + "\u00A0" + title.substring(2);
}
}
return title;
}
private RibbonBandResizePolicy titleResizePolicies(final JRibbonBand ribbonBand) {
return new BaseRibbonBandResizePolicy<AbstractBandControlPanel>(ribbonBand.getControlPanel()) {
@Override
public int getPreferredWidth(int i, int i1) {
return ribbonBand.getGraphics().getFontMetrics(ribbonBand.getFont()).stringWidth(ribbonBand.getTitle()) + 20;
}
@Override
public void install(int i, int i1) {
}
};
}
private List<RibbonBandResizePolicy> getResizePolicies(JRibbonBand ribbonBand) {
final List<RibbonBandResizePolicy> myResizePolicies = new ArrayList<>();
myResizePolicies.add(new CoreRibbonResizePolicies.Mirror(ribbonBand.getControlPanel()));
myResizePolicies.add(titleResizePolicies(ribbonBand));
myResizePolicies.add(new IconRibbonBandResizePolicy(ribbonBand.getControlPanel()));
List<RibbonBandResizePolicy> resizePolicies = new ArrayList<>();
resizePolicies.add(new RibbonBandResizePolicy() {
@Override
public int getPreferredWidth(int i, int i1) {
int pw = 0;
for (RibbonBandResizePolicy p : myResizePolicies) {
int npw = p.getPreferredWidth(i, i1);
if (npw > pw) {
pw = npw;
}
}
return pw;
}
@Override
public void install(int i, int i1) {
for (RibbonBandResizePolicy p : myResizePolicies) {
p.install(i, i1);
}
}
});
return resizePolicies;
}
@Override
protected void loadRecent(ActionEvent evt) {
if (evt.getSource() instanceof JPanel) {
JPanel targetPanel = (JPanel) evt.getSource();
targetPanel.removeAll();
JCommandButtonPanel openHistoryPanel = new JCommandButtonPanel(CommandButtonDisplayState.MEDIUM);
String groupName = translate("menu.recentFiles");
openHistoryPanel.addButtonGroup(groupName);
List<String> recentFiles = Configuration.getRecentFiles();
int j = 0;
for (int i = recentFiles.size() - 1; i >= 0; i--) {
String path = recentFiles.get(i);
RecentFilesButton historyButton = new RecentFilesButton(j + " " + path, null);
historyButton.fileName = path;
historyButton.addActionListener((ActionEvent ae) -> {
RecentFilesButton source = (RecentFilesButton) ae.getSource();
if (Main.openFile(source.fileName, null) == OpenFileResult.NOT_FOUND) {
if (View.showConfirmDialog(null, translate("message.confirm.recentFileNotFound"), translate("message.confirm"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_NO_OPTION) {
Configuration.removeRecentFile(source.fileName);
}
}
});
j++;
historyButton.setHorizontalAlignment(SwingUtilities.LEFT);
openHistoryPanel.addButtonToLastGroup(historyButton);
}
if (recentFiles.isEmpty()) {
JCommandButton emptyLabel = new JCommandButton(translate("menu.recentFiles.empty"));
emptyLabel.setHorizontalAlignment(SwingUtilities.LEFT);
emptyLabel.setEnabled(false);
openHistoryPanel.addButtonToLastGroup(emptyLabel);
}
openHistoryPanel.setMaxButtonColumns(1);
targetPanel.setLayout(new BorderLayout());
targetPanel.add(openHistoryPanel, BorderLayout.CENTER);
}
}
@Override
public void finishMenu(String path) {
if (!menuTitles.containsKey(path)) {
throw new IllegalArgumentException("Menu not started: " + path);
}
boolean isAppMenu = path.equals("_");
String title = menuTitles.get(path);
String icon = menuIcons.get(path);
ActionListener action = menuActions.get(path);
int priority = menuPriorities.get(path);
int type = menuType.get(path);
if (type != TYPE_MENU) {
throw new IllegalArgumentException("Not a menu: " + path);
}
String[] parts = path.contains("/") ? path.split("\\/") : new String[]{""};
List<String> subs = menuSubs.get(path);
boolean onlyCheckboxes = true;
for (String sub : subs) {
if (sub.equals("-")) {
continue;
}
int subType = menuType.get(sub);
if (subType == TYPE_MENUITEM) {
onlyCheckboxes = false;
break;
}
String subIcon = menuIcons.get(sub);
if (subIcon != null) {
onlyCheckboxes = false;
break;
}
}
if (subs.isEmpty()) {
onlyCheckboxes = false;
}
if (isAppMenu) {
RibbonApplicationMenu mainMenu = new RibbonApplicationMenu();
for (String sub : subs) {
if (sub.equals("-")) {
mainMenu.addMenuSeparator();
continue;
}
String subTitle = menuTitles.get(sub);
String subIcon = menuIcons.get(sub);
int subType = menuType.get(sub);
ActionListener subAction = menuActions.get(sub);
final ActionListener subLoader = menuLoaders.get(sub);
if (sub.startsWith("_/$")) //FooterMenu
{
RibbonApplicationMenuEntryFooter footerMenu = new RibbonApplicationMenuEntryFooter(View.getResizableIcon(subIcon, 16), subTitle, subAction);
menuItems.put(sub, footerMenu);
mainMenu.addFooterEntry(footerMenu);
} else {
RibbonApplicationMenuEntryPrimary menu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon(subIcon, 32), subTitle, subAction,
subType == TYPE_MENU ? JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION : JCommandButton.CommandButtonKind.ACTION_ONLY);
if (subLoader != null) {
menu.setRolloverCallback(new RibbonApplicationMenuEntryPrimary.PrimaryRolloverCallback() {
@Override
public void menuEntryActivated(JPanel targetPanel) {
subLoader.actionPerformed(new ActionEvent(targetPanel, 0, "load:" + sub));
}
});
}
menuItems.put(sub, menu);
mainMenu.addMenuEntry(menu);
}
}
ribbon.setApplicationMenu(mainMenu);
return;
}
for (String sub : subs) {
if (sub.equals("-")) {
continue;
}
int subType = menuType.get(sub);
ActionListener subAction = menuActions.get(sub);
String subTitle = menuTitles.get(sub);
String subIcon = menuIcons.get(sub);
String subGroup = menuGroup.get(sub);
HotKey subKey = menuHotkeys.get(sub);
if (subKey != null) {
String keyStr = subKey.toString();
if (keyStr.length() < 8) {
subTitle += " (" + keyStr + ")";
}
}
int subPriority = menuPriorities.get(sub);
final ActionListener subLoader = menuLoaders.get(sub);
AbstractCommandButton but = null;
if (subType == TYPE_MENUITEM || (subType == TYPE_MENU && subAction != null)) {
JCommandButton cbut;
if (subIcon != null) {
cbut = new JCommandButton(fixCommandTitle(subTitle), View.getResizableIcon(subIcon, subPriority == PRIORITY_TOP ? 32 : 16));
} else {
cbut = new JCommandButton(fixCommandTitle(subTitle));
}
if (subKey != null) {
//cbut.setActionRichTooltip(new RichTooltip(subTitle, subKey.toString()));
}
if (subLoader != null) {
cbut.setCommandButtonKind(JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION);
cbut.setPopupCallback(new PopupPanelCallback() {
@Override
public JPopupPanel getPopupPanel(JCommandButton jcb) {
JPopupPanel jp = new JPopupPanel() {
};
subLoader.actionPerformed(new ActionEvent(jp, 0, "load:" + sub));
return jp;
}
});
}
but = cbut;
} else if (subType == TYPE_TOGGLEMENUITEM) {
if (onlyCheckboxes) {
JCheckBox cb = new JCheckBox(subTitle);
if (subAction != null) {
cb.addActionListener(subAction);
}
menuItems.put(sub, cb);
} else {
if (subIcon != null) {
but = new JCommandToggleButton(fixCommandTitle(subTitle), View.getResizableIcon(subIcon, subPriority == PRIORITY_TOP ? 32 : 16));
} else {
but = new JCommandToggleButton(fixCommandTitle(subTitle));
}
menuToToggleGroup.get(sub).add((JCommandToggleButton) but);
}
}
if (but != null) {
if (subAction != null) {
but.addActionListener(subAction);
}
menuItems.put(sub, but);
}
}
//if (parts.length == 3)
{ //3rd level - it's a Band!
JRibbonBand band = new JRibbonBand(title, icon != null ? View.getResizableIcon(icon, 16) : null, null);
band.setResizePolicies(getResizePolicies(band));
int cnt = 0;
for (String sub : subs) {
if (sub.equals("-")) {
continue;
}
Object o = menuItems.get(sub);
int subPriority = menuPriorities.get(sub);
int subType = menuType.get(sub);
ActionListener subAction = menuActions.get(sub);
if (subType != TYPE_MENU || (subAction != null)) {
if (o instanceof AbstractCommandButton) {
RibbonElementPriority ribbonPriority = RibbonElementPriority.MEDIUM;
switch (subPriority) {
case PRIORITY_LOW:
ribbonPriority = RibbonElementPriority.LOW;
break;
case PRIORITY_MEDIUM:
ribbonPriority = RibbonElementPriority.MEDIUM;
break;
case PRIORITY_TOP:
ribbonPriority = RibbonElementPriority.TOP;
break;
}
band.addCommandButton((AbstractCommandButton) o, ribbonPriority);
cnt++;
} else if (o instanceof JComponent) {
band.addRibbonComponent(new JRibbonComponent((JComponent) o));
cnt++;
}
}
}
if (cnt > 0) {
if (parts.length != 3) {
if (!menuSubs.containsKey(path)) {
menuSubs.put(path, new ArrayList<>());
}
if (!menuSubs.get(path).contains(path + "/_")) {
menuSubs.get(path).add(0, path + "/_");
}
menuItems.put(path + "/_", band);
} else {
menuItems.put(path, band);
}
}
}
if (parts.length == 1) { //1st level - it's ribbon
for (String sub : subs) {
if (sub.equals("-")) {
continue;
}
if (menuItems.get(sub) instanceof RibbonTask) {
RibbonTask rt = (RibbonTask) menuItems.get(sub);
if (menuOptional.get(sub)) {
RibbonContextualTaskGroup rct = new RibbonContextualTaskGroup("", new Color(128, 0, 0), rt);
ribbon.addContextualTaskGroup(rct);
optionalGroups.put(sub, rct);
//ribbon.setVisible(rct, false);
} else {
ribbon.addTask(rt);
}
}
}
} else if (parts.length == 2) { //2nd level - it's a Task!
int bandCount = 0;
for (String sub : subs) {
if (sub.equals("-")) {
continue;
}
if (menuItems.get(sub) instanceof AbstractRibbonBand) {
bandCount++;
}
}
AbstractRibbonBand[] bands = new AbstractRibbonBand[bandCount];
int b = 0;
for (String sub : subs) {
if (sub.equals("-")) {
continue;
}
if (menuItems.get(sub) instanceof AbstractRibbonBand) {
bands[b++] = (AbstractRibbonBand) menuItems.get(sub);
}
}
if (bands.length > 0) {
RibbonTask task = new RibbonTask(title, bands);
menuItems.put(path, task);
}
}
}
@Override
public void addMenuItem(String path, String title, String icon, ActionListener action, int priority, ActionListener subLoader, boolean isLeaf, HotKey key, boolean isOptional) {
String parentPath = path.contains("/") ? path.substring(0, path.lastIndexOf('/')) : "";
if (!menuSubs.containsKey(parentPath)) {
throw new IllegalArgumentException("No parent menu exists: " + parentPath);
}
menuOptional.put(path, isOptional);
menuHotkeys.put(path, key);
menuSubs.get(parentPath).add(path);
if (!isLeaf) {
menuSubs.put(path, new ArrayList<>());
}
menuLoaders.put(path, subLoader);
menuTitles.put(path, title);
menuIcons.put(path, icon);
menuActions.put(path, action);
menuPriorities.put(path, priority);
menuType.put(path, isLeaf ? TYPE_MENUITEM : TYPE_MENU);
}
@Override
public void addToggleMenuItem(String path, String title, String group, String icon, ActionListener action, int priority, HotKey key) {
addMenuItem(path, title, icon, action, priority, action, true, key, false);
menuType.put(path, TYPE_TOGGLEMENUITEM);
menuGroup.put(path, group);
if (group == null) {
group = path;
}
if (!menuToggleGroups.containsKey(group)) {
menuToggleGroups.put(group, new CommandToggleButtonGroup());
}
menuToToggleGroup.put(path, menuToggleGroups.get(group));
}
@Override
public boolean isMenuChecked(String path) {
Object o = menuItems.get(path);
if (o instanceof JCommandToggleButton) {
JCommandToggleButton t = (JCommandToggleButton) o;
if (!menuToToggleGroup.containsKey(path)) {
throw new IllegalArgumentException("No toggle group for " + path);
}
return menuToToggleGroup.get(path).getSelected() == t;
}
if (o instanceof JCheckBox) {
return ((JCheckBox) o).isSelected();
}
throw new IllegalArgumentException("Not a toggle menu");
}
@Override
public void setMenuChecked(String path, boolean checked) {
Object o = menuItems.get(path);
if (o instanceof JCommandToggleButton) {
JCommandToggleButton t = (JCommandToggleButton) o;
if (!menuToToggleGroup.containsKey(path)) {
throw new IllegalArgumentException("No toggle group for " + path);
}
menuToToggleGroup.get(path).setSelected(t, checked);
} else if (o instanceof JToggleButton) {
((JToggleButton) o).setSelected(checked);
} else {
throw new IllegalArgumentException("Not a toggle menu");
}
}
@Override
public void setGroupSelection(String group, String selected) {
if (!menuToggleGroups.containsKey(group)) {
throw new IllegalArgumentException("Group " + group + " does not exist");
}
menuToggleGroups.get(group).clearSelection();
if (selected == null) {
return;
}
if (!menuItems.containsKey(selected)) {
throw new IllegalArgumentException("Selection " + selected + " not found");
}
JCommandToggleButton c = (JCommandToggleButton) menuItems.get(selected);
menuToggleGroups.get(group).setSelected(c, true);
}
@Override
public String getGroupSelection(String group) {
if (!menuToggleGroups.containsKey(group)) {
throw new IllegalArgumentException("Group " + group + " does not exist");
}
JCommandToggleButton c = menuToggleGroups.get(group).getSelected();
for (String path : menuItems.keySet()) {
if (menuItems.get(path) == c) {
return path;
}
}
return null;
}
@Override
public void clearMenu(String path) {
}
@Override
public void setMenuEnabled(String path, boolean enabled) {
if (!menuItems.containsKey(path)) {
throw new IllegalArgumentException("Menu not found: " + path);
}
Object o = menuItems.get(path);
try {
if (o instanceof JRibbonBand) {
((JRibbonBand) o).setEnabled(enabled);
} else if (o instanceof AbstractCommandButton) {
((AbstractCommandButton) o).setEnabled(enabled);
} else if (o instanceof RibbonApplicationMenuEntryPrimary) {
((RibbonApplicationMenuEntryPrimary) o).setEnabled(enabled);
} else if (o instanceof RibbonApplicationMenuEntryFooter) {
((RibbonApplicationMenuEntryFooter) o).setEnabled(enabled);
} else if (o instanceof JComponent) {
((JComponent) o).setEnabled(enabled);
} else {
throw new IllegalArgumentException("Cannot set enabled to: " + path);
}
} catch (Exception ex) {
//some substance issues, ignore
}
}
@Override
public void initMenu() {
menuSubs.put("", new ArrayList<>());
menuPriorities.put("", 0);
menuActions.put("", null);
menuTitles.put("", null);
menuIcons.put("", null);
menuType.put("", TYPE_MENU);
}
@Override
public void addSeparator(String parentPath) {
if (!menuSubs.containsKey(parentPath)) {
throw new IllegalArgumentException("Menu does not exist: " + parentPath);
}
menuSubs.get(parentPath).add("-");
}
@Override
public boolean supportsMenuAction() {
return true;
}
@Override
public boolean supportsAppMenu() {
return true;
}
@Override
public void setPathVisible(String path, boolean val) {
Object o = menuItems.get(path);
if (o instanceof RibbonTask) {
if (menuOptional.get(path)) {
RibbonContextualTaskGroup rg = optionalGroups.get(path);
if (ribbon.isVisible(rg) != val) {
View.execInEventDispatch(new Runnable() {
@Override
public void run() {
try {
ribbon.setVisible(rg, val);
} catch (Exception ex) {
}
}
});
}
}
}
}
@Override
public void hilightPath(String path) {
Object o = menuItems.get(path);
if (o instanceof RibbonTask) {
if (menuOptional.get(path)) {
View.execInEventDispatch(new Runnable() {
@Override
public void run() {
if (!ribbon.isVisible(optionalGroups.get(path))) {
ribbon.setVisible(optionalGroups.get(path), true);
}
ribbon.setSelectedTask((RibbonTask) o);
}
});
return;
}
final RibbonTask rt = (RibbonTask) o;
View.execInEventDispatch(new Runnable() {
@Override
public void run() {
ribbon.setSelectedTask(rt);
}
});
}
}
}