/*******************************************************************************
* Copyright (c) 2015 - 2016
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*******************************************************************************/
package jsettlers.mapcreator.main.window;
import java.awt.BorderLayout;
import java.awt.Desktop;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Properties;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import jsettlers.exceptionhandler.ExceptionHandler;
import jsettlers.mapcreator.localization.EditorLabels;
/**
* Editor JFrame, Main Wndow
*
* @author Andreas Butti
*/
public abstract class EditorFrame extends JFrame {
private static final long serialVersionUID = 1L;
/**
* Menu / Toolbar configuration
*/
private final Properties menuconfig = new Properties();
/**
* Shortcut configuration
*/
private final Properties shortcut = new Properties();
/**
* Split editor / sidebar
*/
private final JSplitPane splitter;
/**
* Display icon AND text in toolbar, default display only icon and text as tooltip
*/
public static final String DISPLAY_TEXT_IN_TOOLBAR = "display-text-in-toolbar";
/**
* Display as Check menu
*/
public static final String DISPLAY_CHECKBOX = "display-checkbox";
/**
* Value of the checkbox (for DISPLAY_CHECKBOX)
*/
public static final String CHECKBOX_VALUE = "checkbox-value";
/**
* Logo for windows
*/
public static final Image APP_ICON = new ImageIcon(EditorFrame.class.getResource("icon.png")).getImage();
/**
* Constructor
*
* @param root
* Root panel
* @param sidebar
* Sidebar panel
*/
public EditorFrame(JComponent root, JComponent sidebar) {
super();
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
setFilename("unnamed");
setLayout(new BorderLayout());
setIconImage(APP_ICON);
registerActions();
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
ActionMap actionMap = ((JPanel) getContentPane()).getActionMap();
Action quitAction = actionMap.get("quit");
quitAction.actionPerformed(new ActionEvent(this, 0, "quit"));
}
});
this.splitter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, root, sidebar);
splitter.setResizeWeight(1);
add(splitter, BorderLayout.CENTER);
splitter.setDividerLocation(980);
}
/**
* @return Split editor / sidebar
*/
public JSplitPane getSplitter() {
return splitter;
}
/**
* Initialize menubar and toolbar
*/
public void initMenubarAndToolbar() {
createMenubar();
createToolbar();
}
/**
* Register actions
*/
private void registerActions() {
registerAction("manual", new AbstractAction() {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
try {
Desktop.getDesktop().browse(new URI("http://www.settlers-android-clone.com/the-map-editor/"));
} catch (IOException | URISyntaxException e1) {
ExceptionHandler.displayError(e1, "Could not open URL");
}
}
});
registerAction("about", new AbstractAction() {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
AboutDialog dlg = new AboutDialog(EditorFrame.this);
dlg.setVisible(true);
}
});
}
/**
* Register a menu / toolbar action
*
* @param actionName
* Name of the action
* @param action
* Action to execute
*/
public void registerAction(String actionName, Action action) {
ActionMap actionMap = ((JPanel) this.getContentPane()).getActionMap();
// try to load icon, if any
URL icon = EditorFrame.class.getResource("icons/" + actionName + ".png");
if (icon != null) {
action.putValue(Action.SMALL_ICON, new ImageIcon(icon));
}
action.putValue(Action.NAME, EditorLabels.getLabel("action." + actionName));
actionMap.put(actionName, action);
}
/**
* Enable / disable an action
*
* @param actionName
* Action name
* @param enable
* enabled / disabled
*/
public void enableAction(String actionName, boolean enable) {
ActionMap actionMap = ((JPanel) this.getContentPane()).getActionMap();
Action action = actionMap.get(actionName);
if (action == null) {
System.err.println("Could not find action \"" + action + "\" to enable / disable");
return;
}
action.setEnabled(enable);
}
/**
* Create the menu from menu.properties
*/
private void createMenubar() {
try {
menuconfig.load(EditorFrame.class.getResourceAsStream("menu.properties"));
} catch (Exception e) {
e.printStackTrace();
System.err.println("Could not load menu.properties");
}
try {
shortcut.load(EditorFrame.class.getResourceAsStream("shortcut.properties"));
} catch (Exception e) {
e.printStackTrace();
System.err.println("Could not load shortcut.properties");
}
JMenuBar menuBar = new JMenuBar();
for (String menuName : menuconfig.getProperty("menubar", "").split(",")) {
menuName = menuName.trim();
if (menuName.isEmpty()) {
continue;
}
JMenu menu = createMenu(menuName);
menuBar.add(menu);
}
setJMenuBar(menuBar);
}
/**
* Create a single menu in the Menubar
*
* @param menuName
* Name of the menu
* @return JMenu
*/
private JMenu createMenu(String menuName) {
ActionMap actionMap = ((JPanel) this.getContentPane()).getActionMap();
JMenu menu = new JMenu(EditorLabels.getLabel("menu." + menuName));
// because of the open gl context
menu.getPopupMenu().setLightWeightPopupEnabled(false);
for (String menuActionName : menuconfig.getProperty("menu." + menuName, "").split(",")) {
menuActionName = menuActionName.trim();
if (menuActionName.isEmpty()) {
continue;
}
if ("---".equals(menuActionName)) {
menu.addSeparator();
} else {
final Action action = actionMap.get(menuActionName);
if (action == null) {
System.err.println("Action \"" + menuActionName + "\" not found!");
continue;
}
createMenuItemForAction(action, menuActionName, menu);
}
}
return menu;
}
/**
* Create a menu item for a specific action
*
* @param action
* The action
* @param menuActionName
* The name of the action
* @param menu
* The menu to add the action
*/
private void createMenuItemForAction(final Action action, String menuActionName, JMenu menu) {
final JMenuItem it;
Boolean displayAsCheckbox = (Boolean) action.getValue(EditorFrame.DISPLAY_CHECKBOX);
if (displayAsCheckbox != null && displayAsCheckbox) {
it = createCheckboxMenuItemForAction(action);
menu.add(it);
} else {
it = menu.add(action);
}
action.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (Action.NAME.equals(evt.getPropertyName())) {
it.setText((String) evt.getNewValue());
}
}
});
it.setText((String) action.getValue(Action.NAME));
String shortcut = this.shortcut.getProperty(menuActionName);
if (shortcut != null) {
it.setAccelerator(
KeyStroke.getKeyStroke(shortcut));
}
}
/**
* Create a checkbox menu item
*
* @param action
* The target action
*
* @return JMenuItem
*/
private JMenuItem createCheckboxMenuItemForAction(final Action action) {
final JCheckBoxMenuItem it = new JCheckBoxMenuItem();
it.setAction(action);
it.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
Object oldValue = action.getValue(EditorFrame.CHECKBOX_VALUE);
if (oldValue != null && oldValue.equals(it.isSelected())) {
return;
}
action.putValue(EditorFrame.CHECKBOX_VALUE, it.isSelected());
action.actionPerformed(new ActionEvent(it, 0, "changed"));
}
});
action.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (EditorFrame.CHECKBOX_VALUE.equals(evt.getPropertyName())) {
Boolean checked = (Boolean) evt.getNewValue();
if (it.isSelected() != checked) {
it.setSelected(checked);
}
}
}
});
return it;
}
/**
* Create the toolbar from menu.properties
*/
private void createToolbar() {
ActionMap actionMap = ((JPanel) this.getContentPane()).getActionMap();
JToolBar tb = new JToolBar();
tb.setFloatable(false);
for (String toolName : menuconfig.getProperty("toolbar", "").split(",")) {
toolName = toolName.trim();
if (toolName.isEmpty()) {
continue;
}
if ("---".equals(toolName)) {
tb.addSeparator();
} else if ("player-spinner".equals(toolName)) {
tb.add(new JLabel(EditorLabels.getLabel("window.current-player")));
JComponent playerSpinner = createPlayerSelectSelection();
tb.add(playerSpinner);
} else {
final Action action = actionMap.get(toolName);
if (action == null) {
System.err.println("Action \"" + toolName + "\" not found!");
continue;
}
final JButton bt = tb.add(action);
action.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (Action.NAME.equals(evt.getPropertyName())) {
setButtonText(bt, action);
}
}
});
setButtonText(bt, action);
bt.setVerticalTextPosition(SwingConstants.CENTER);
bt.setHorizontalTextPosition(SwingConstants.RIGHT);
}
}
add(tb, BorderLayout.NORTH);
}
/**
* Set the text of a button
*
* @param bt
* Button
* @param action
* Action
*/
private void setButtonText(JButton bt, Action action) {
Boolean displayTextInToolbar = (Boolean) action.getValue(EditorFrame.DISPLAY_TEXT_IN_TOOLBAR);
if (displayTextInToolbar != null && displayTextInToolbar) {
bt.setText((String) action.getValue(Action.NAME));
} else {
bt.setToolTipText((String) action.getValue(Action.NAME));
}
bt.setName((String) action.getValue(Action.NAME));
}
/**
* Create the player selection
*
* @return JComponent
*/
protected abstract JComponent createPlayerSelectSelection();
/**
* @param filename
* Filename to display in the header
*/
public void setFilename(String filename) {
setTitle(String.format(EditorLabels.getLabel("window.header"), filename));
}
}