001 // License: GPL. Copyright 2007 by Immanuel Scholz and others
002 package org.openstreetmap.josm.gui.preferences;
003
004 import static org.openstreetmap.josm.tools.I18n.tr;
005
006 import java.awt.Component;
007 import java.awt.Container;
008 import java.awt.Dimension;
009 import java.awt.GridBagLayout;
010 import java.awt.GridLayout;
011 import java.awt.LayoutManager;
012 import java.awt.Rectangle;
013 import java.awt.datatransfer.DataFlavor;
014 import java.awt.datatransfer.Transferable;
015 import java.awt.datatransfer.UnsupportedFlavorException;
016 import java.awt.event.ActionEvent;
017 import java.awt.event.ActionListener;
018 import java.awt.event.InputEvent;
019 import java.beans.PropertyChangeEvent;
020 import java.beans.PropertyChangeListener;
021 import java.io.IOException;
022 import java.util.ArrayList;
023 import java.util.Arrays;
024 import java.util.Collection;
025 import java.util.Collections;
026 import java.util.HashMap;
027 import java.util.LinkedList;
028 import java.util.List;
029 import java.util.Map;
030
031 import javax.swing.Action;
032 import javax.swing.DefaultListCellRenderer;
033 import javax.swing.DefaultListModel;
034 import javax.swing.Icon;
035 import javax.swing.ImageIcon;
036 import javax.swing.JButton;
037 import javax.swing.JComponent;
038 import javax.swing.JLabel;
039 import javax.swing.JList;
040 import javax.swing.JMenuItem;
041 import javax.swing.JPanel;
042 import javax.swing.JPopupMenu;
043 import javax.swing.JScrollPane;
044 import javax.swing.JTable;
045 import javax.swing.JToolBar;
046 import javax.swing.JTree;
047 import javax.swing.ListCellRenderer;
048 import javax.swing.MenuElement;
049 import javax.swing.TransferHandler;
050 import javax.swing.event.ListSelectionEvent;
051 import javax.swing.event.ListSelectionListener;
052 import javax.swing.event.TreeSelectionEvent;
053 import javax.swing.event.TreeSelectionListener;
054 import javax.swing.table.AbstractTableModel;
055 import javax.swing.tree.DefaultMutableTreeNode;
056 import javax.swing.tree.DefaultTreeCellRenderer;
057 import javax.swing.tree.DefaultTreeModel;
058 import javax.swing.tree.TreePath;
059
060 import org.openstreetmap.josm.Main;
061 import org.openstreetmap.josm.actions.ActionParameter;
062 import org.openstreetmap.josm.actions.AdaptableAction;
063 import org.openstreetmap.josm.actions.ParameterizedAction;
064 import org.openstreetmap.josm.actions.ParameterizedActionDecorator;
065 import org.openstreetmap.josm.gui.tagging.TaggingPreset;
066 import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
067 import org.openstreetmap.josm.tools.GBC;
068 import org.openstreetmap.josm.tools.ImageProvider;
069
070 public class ToolbarPreferences implements PreferenceSettingFactory {
071
072
073 private static final String EMPTY_TOOLBAR_MARKER = "<!-empty-!>";
074
075 public static class ActionDefinition {
076 private final Action action;
077 private String name = "";
078 private String icon = "";
079 private ImageIcon ico = null;
080 private final Map<String, Object> parameters = new HashMap<String, Object>();
081
082 public ActionDefinition(Action action) {
083 this.action = action;
084 }
085
086 public Map<String, Object> getParameters() {
087 return parameters;
088 }
089
090 public Action getParametrizedAction() {
091 if (getAction() instanceof ParameterizedAction)
092 return new ParameterizedActionDecorator((ParameterizedAction) getAction(), parameters);
093 else
094 return getAction();
095 }
096
097 public Action getAction() {
098 return action;
099 }
100
101 public String getName() {
102 return name;
103 }
104
105 public String getDisplayName() {
106 return name.isEmpty() ? (String) action.getValue(Action.NAME) : name;
107 }
108
109 public String getDisplayTooltip() {
110 if(!name.isEmpty())
111 return name;
112
113 Object tt = action.getValue(TaggingPreset.OPTIONAL_TOOLTIP_TEXT);
114 if (tt != null)
115 return (String) tt;
116
117 return (String) action.getValue(Action.SHORT_DESCRIPTION);
118 }
119
120 public Icon getDisplayIcon() {
121 return ico != null ? ico : (Icon) action.getValue(Action.SMALL_ICON);
122 }
123
124 public void setName(String name) {
125 this.name = name;
126 }
127
128 public String getIcon() {
129 return icon;
130 }
131
132 public void setIcon(String icon) {
133 this.icon = icon;
134 ico = ImageProvider.getIfAvailable("", icon);
135 }
136
137 public boolean isSeparator() {
138 return action == null;
139 }
140
141 public static ActionDefinition getSeparator() {
142 return new ActionDefinition(null);
143 }
144 }
145
146 public static class ActionParser {
147 private final Map<String, Action> actions;
148 private final StringBuilder result = new StringBuilder();
149 private int index;
150 private char[] s;
151
152 public ActionParser(Map<String, Action> actions) {
153 this.actions = actions;
154 }
155
156 private String readTillChar(char ch1, char ch2) {
157 result.setLength(0);
158 while (index < s.length && s[index] != ch1 && s[index] != ch2) {
159 if (s[index] == '\\') {
160 index++;
161 if (index >= s.length) {
162 break;
163 }
164 }
165 result.append(s[index]);
166 index++;
167 }
168 return result.toString();
169 }
170
171 private void skip(char ch) {
172 if (index < s.length && s[index] == ch) {
173 index++;
174 }
175 }
176
177 public ActionDefinition loadAction(String actionName) {
178 index = 0;
179 this.s = actionName.toCharArray();
180
181 String name = readTillChar('(', '{');
182 Action action = actions.get(name);
183
184 if (action == null)
185 return null;
186
187 ActionDefinition result = new ActionDefinition(action);
188
189 if (action instanceof ParameterizedAction) {
190 skip('(');
191
192 ParameterizedAction parametrizedAction = (ParameterizedAction)action;
193 Map<String, ActionParameter<?>> actionParams = new HashMap<String, ActionParameter<?>>();
194 for (ActionParameter<?> param: parametrizedAction.getActionParameters()) {
195 actionParams.put(param.getName(), param);
196 }
197
198 while (index < s.length && s[index] != ')') {
199 String paramName = readTillChar('=', '=');
200 skip('=');
201 String paramValue = readTillChar(',',')');
202 if (paramName.length() > 0) {
203 ActionParameter<?> actionParam = actionParams.get(paramName);
204 if (actionParam != null) {
205 result.getParameters().put(paramName, actionParam.readFromString(paramValue));
206 }
207 }
208 skip(',');
209 }
210 skip(')');
211 }
212 if (action instanceof AdaptableAction) {
213 skip('{');
214
215 while (index < s.length && s[index] != '}') {
216 String paramName = readTillChar('=', '=');
217 skip('=');
218 String paramValue = readTillChar(',','}');
219 if ("icon".equals(paramName) && paramValue.length() > 0)
220 result.setIcon(paramValue);
221 else if("name".equals(paramName) && paramValue.length() > 0)
222 result.setName(paramValue);
223 skip(',');
224 }
225 skip('}');
226 }
227
228 return result;
229 }
230
231 private void escape(String s) {
232 for (int i=0; i<s.length(); i++) {
233 char ch = s.charAt(i);
234 if (ch == '\\' || ch == '(' || ch == '{' || ch == ',' || ch == ')' || ch == '}' || ch == '=') {
235 result.append('\\');
236 result.append(ch);
237 } else {
238 result.append(ch);
239 }
240 }
241 }
242
243 @SuppressWarnings("unchecked")
244 public String saveAction(ActionDefinition action) {
245 result.setLength(0);
246
247 String val = (String) action.getAction().getValue("toolbar");
248 if(val == null)
249 return null;
250 escape(val);
251 if (action.getAction() instanceof ParameterizedAction) {
252 result.append('(');
253 List<ActionParameter<?>> params = ((ParameterizedAction)action.getAction()).getActionParameters();
254 for (int i=0; i<params.size(); i++) {
255 ActionParameter<Object> param = (ActionParameter<Object>)params.get(i);
256 escape(param.getName());
257 result.append('=');
258 Object value = action.getParameters().get(param.getName());
259 if (value != null) {
260 escape(param.writeToString(value));
261 }
262 if (i < params.size() - 1) {
263 result.append(',');
264 } else {
265 result.append(')');
266 }
267 }
268 }
269 if (action.getAction() instanceof AdaptableAction) {
270 boolean first = true;
271 String tmp = action.getName();
272 if(tmp.length() != 0) {
273 result.append(first ? "{" : ",");
274 result.append("name=");
275 escape(tmp);
276 first = false;
277 }
278 tmp = action.getIcon();
279 if(tmp.length() != 0) {
280 result.append(first ? "{" : ",");
281 result.append("icon=");
282 escape(tmp);
283 first = false;
284 }
285 if(!first)
286 result.append('}');
287 }
288
289 return result.toString();
290 }
291 }
292
293 private static class ActionParametersTableModel extends AbstractTableModel {
294
295 private ActionDefinition currentAction = ActionDefinition.getSeparator();
296
297 public int getColumnCount() {
298 return 2;
299 }
300
301 public int getRowCount() {
302 int adaptable = ((currentAction.getAction() instanceof AdaptableAction) ? 2 : 0);
303 if (currentAction.isSeparator() || !(currentAction.getAction() instanceof ParameterizedAction))
304 return adaptable;
305 ParameterizedAction pa = (ParameterizedAction)currentAction.getAction();
306 return pa.getActionParameters().size() + adaptable;
307 }
308
309 @SuppressWarnings("unchecked")
310 private ActionParameter<Object> getParam(int index) {
311 ParameterizedAction pa = (ParameterizedAction)currentAction.getAction();
312 return (ActionParameter<Object>) pa.getActionParameters().get(index);
313 }
314
315 public Object getValueAt(int rowIndex, int columnIndex) {
316 if(currentAction.getAction() instanceof AdaptableAction)
317 {
318 if (rowIndex < 2) {
319 switch (columnIndex) {
320 case 0:
321 return rowIndex == 0 ? tr("Tooltip") : tr("Icon");
322 case 1:
323 return rowIndex == 0 ? currentAction.getName() : currentAction.getIcon();
324 default:
325 return null;
326 }
327 } else
328 rowIndex -= 2;
329 }
330 ActionParameter<Object> param = getParam(rowIndex);
331 switch (columnIndex) {
332 case 0:
333 return param.getName();
334 case 1:
335 return param.writeToString(currentAction.getParameters().get(param.getName()));
336 default:
337 return null;
338 }
339 }
340
341 @Override
342 public boolean isCellEditable(int row, int column) {
343 return column == 1;
344 }
345
346 @Override
347 public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
348 if(currentAction.getAction() instanceof AdaptableAction)
349 {
350 if (rowIndex == 0) {
351 currentAction.setName((String)aValue);
352 return;
353 } else if (rowIndex == 1) {
354 currentAction.setIcon((String)aValue);
355 return;
356 } else
357 rowIndex -= 2;
358 }
359 ActionParameter<Object> param = getParam(rowIndex);
360 currentAction.getParameters().put(param.getName(), param.readFromString((String)aValue));
361 }
362
363
364 public void setCurrentAction(ActionDefinition currentAction) {
365 this.currentAction = currentAction;
366 fireTableDataChanged();
367 }
368
369 }
370
371 private static class ToolbarPopupMenu extends JPopupMenu {
372 public ToolbarPopupMenu(final ActionDefinition action) {
373
374 if(action != null) {
375 add(tr("Remove from toolbar",action.getDisplayName()))
376 .addActionListener(new ActionListener() {
377 public void actionPerformed(ActionEvent e) {
378 Collection<String> t = new LinkedList<String>(getToolString());
379 ActionParser parser = new ActionParser(null);
380 // get text definition of current action
381 String res = parser.saveAction(action);
382 // remove the button from toolbar preferences
383 t.remove( res );
384 Main.pref.putCollection("toolbar", t);
385 Main.toolbar.refreshToolbarControl();
386 }
387 });
388 }
389
390 add(tr("Configure toolbar")).addActionListener(new ActionListener() {
391 public void actionPerformed(ActionEvent e) {
392 final PreferenceDialog p =new PreferenceDialog(Main.parent);
393 p.selectPreferencesTabByName("toolbar");
394 p.setVisible(true);
395 }
396 });
397
398 }
399 }
400
401 /**
402 * Key: Registered name (property "toolbar" of action).
403 * Value: The action to execute.
404 */
405 private Map<String, Action> actions = new HashMap<String, Action>();
406 private Map<String, Action> regactions = new HashMap<String, Action>();
407
408 private DefaultMutableTreeNode rootActionsNode = new DefaultMutableTreeNode(tr("Actions"));
409
410 public JToolBar control = new JToolBar();
411
412 public PreferenceSetting createPreferenceSetting() {
413 return new Settings(rootActionsNode);
414 }
415
416 public class Settings extends DefaultTabPreferenceSetting {
417
418 private final class Move implements ActionListener {
419 public void actionPerformed(ActionEvent e) {
420 if (e.getActionCommand().equals("<") && actionsTree.getSelectionCount() > 0) {
421
422 int leadItem = selected.getSize();
423 if (selectedList.getSelectedIndex() != -1) {
424 int[] indices = selectedList.getSelectedIndices();
425 leadItem = indices[indices.length - 1];
426 }
427 for (TreePath selectedAction : actionsTree.getSelectionPaths()) {
428 DefaultMutableTreeNode node = (DefaultMutableTreeNode) selectedAction.getLastPathComponent();
429 if (node.getUserObject() == null) {
430 selected.add(leadItem++, ActionDefinition.getSeparator());
431 } else if (node.getUserObject() instanceof Action) {
432 selected.add(leadItem++, new ActionDefinition((Action)node.getUserObject()));
433
434 }
435 }
436 } else if (e.getActionCommand().equals(">") && selectedList.getSelectedIndex() != -1) {
437 while (selectedList.getSelectedIndex() != -1) {
438 selected.remove(selectedList.getSelectedIndex());
439 }
440 } else if (e.getActionCommand().equals("up")) {
441 int i = selectedList.getSelectedIndex();
442 Object o = selected.get(i);
443 if (i != 0) {
444 selected.remove(i);
445 selected.add(i-1, o);
446 selectedList.setSelectedIndex(i-1);
447 }
448 } else if (e.getActionCommand().equals("down")) {
449 int i = selectedList.getSelectedIndex();
450 Object o = selected.get(i);
451 if (i != selected.size()-1) {
452 selected.remove(i);
453 selected.add(i+1, o);
454 selectedList.setSelectedIndex(i+1);
455 }
456 }
457 }
458 }
459
460 private class ActionTransferable implements Transferable {
461
462 private DataFlavor[] flavors = new DataFlavor[] { ACTION_FLAVOR };
463
464 private final List<ActionDefinition> actions;
465
466 public ActionTransferable(List<ActionDefinition> actions) {
467 this.actions = actions;
468 }
469
470 public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
471 return actions;
472 }
473
474 public DataFlavor[] getTransferDataFlavors() {
475 return flavors;
476 }
477
478 public boolean isDataFlavorSupported(DataFlavor flavor) {
479 return flavors[0] == flavor;
480 }
481 }
482
483 private final Move moveAction = new Move();
484
485 private final DefaultListModel selected = new DefaultListModel();
486 private final JList selectedList = new JList(selected);
487
488 private final DefaultTreeModel actionsTreeModel;
489 private final JTree actionsTree;
490
491 private final ActionParametersTableModel actionParametersModel = new ActionParametersTableModel();
492 private final JTable actionParametersTable = new JTable(actionParametersModel);
493 private JPanel actionParametersPanel;
494
495 private JButton upButton;
496 private JButton downButton;
497 private JButton removeButton;
498 private JButton addButton;
499
500 private String movingComponent;
501
502 public Settings(DefaultMutableTreeNode rootActionsNode) {
503 super("toolbar", tr("Toolbar customization"), tr("Customize the elements on the toolbar."));
504 actionsTreeModel = new DefaultTreeModel(rootActionsNode);
505 actionsTree = new JTree(actionsTreeModel);
506 }
507
508 private JButton createButton(String name) {
509 JButton b = new JButton();
510 if (name.equals("up")) {
511 b.setIcon(ImageProvider.get("dialogs", "up"));
512 } else if (name.equals("down")) {
513 b.setIcon(ImageProvider.get("dialogs", "down"));
514 } else {
515 b.setText(name);
516 }
517 b.addActionListener(moveAction);
518 b.setActionCommand(name);
519 return b;
520 }
521
522 private void updateEnabledState() {
523 int index = selectedList.getSelectedIndex();
524 upButton.setEnabled(index > 0);
525 downButton.setEnabled(index != -1 && index < selectedList.getModel().getSize() - 1);
526 removeButton.setEnabled(index != -1);
527 addButton.setEnabled(actionsTree.getSelectionCount() > 0);
528 }
529
530 public void addGui(PreferenceTabbedPane gui) {
531 actionsTree.setCellRenderer(new DefaultTreeCellRenderer() {
532 @Override
533 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
534 boolean leaf, int row, boolean hasFocus) {
535 DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
536 JLabel comp = (JLabel) super.getTreeCellRendererComponent(
537 tree, value, sel, expanded, leaf, row, hasFocus);
538 if (node.getUserObject() == null) {
539 comp.setText(tr("Separator"));
540 comp.setIcon(ImageProvider.get("preferences/separator"));
541 }
542 else if (node.getUserObject() instanceof Action) {
543 Action action = (Action) node.getUserObject();
544 comp.setText((String) action.getValue(Action.NAME));
545 comp.setIcon((Icon) action.getValue(Action.SMALL_ICON));
546 }
547 return comp;
548 }
549 });
550
551 ListCellRenderer renderer = new DefaultListCellRenderer(){
552 @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
553 String s;
554 Icon i;
555 ActionDefinition action = (ActionDefinition)value;
556 if (!action.isSeparator()) {
557 s = action.getDisplayName();
558 i = action.getDisplayIcon();
559 } else {
560 i = ImageProvider.get("preferences/separator");
561 s = tr("Separator");
562 }
563 JLabel l = (JLabel)super.getListCellRendererComponent(list, s, index, isSelected, cellHasFocus);
564 l.setIcon(i);
565 return l;
566 }
567 };
568 selectedList.setCellRenderer(renderer);
569 selectedList.addListSelectionListener(new ListSelectionListener(){
570 public void valueChanged(ListSelectionEvent e) {
571 boolean sel = selectedList.getSelectedIndex() != -1;
572 if (sel) {
573 actionsTree.clearSelection();
574 ActionDefinition action = (ActionDefinition) selected.get(selectedList.getSelectedIndex());
575 actionParametersModel.setCurrentAction(action);
576 actionParametersPanel.setVisible(actionParametersModel.getRowCount() > 0);
577 }
578 updateEnabledState();
579 }
580 });
581
582 selectedList.setDragEnabled(true);
583 selectedList.setTransferHandler(new TransferHandler() {
584 @Override
585 protected Transferable createTransferable(JComponent c) {
586 List<ActionDefinition> actions = new ArrayList<ActionDefinition>();
587 for (Object o: ((JList)c).getSelectedValues()) {
588 actions.add((ActionDefinition)o);
589 }
590 return new ActionTransferable(actions);
591 }
592
593 @Override
594 public int getSourceActions(JComponent c) {
595 return TransferHandler.MOVE;
596 }
597
598 @Override
599 public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
600 for (DataFlavor f : transferFlavors) {
601 if (ACTION_FLAVOR.equals(f))
602 return true;
603 }
604 return false;
605 }
606
607 @Override
608 public void exportAsDrag(JComponent comp, InputEvent e, int action) {
609 super.exportAsDrag(comp, e, action);
610 movingComponent = "list";
611 }
612
613 @Override
614 public boolean importData(JComponent comp, Transferable t) {
615 try {
616 int dropIndex = selectedList.locationToIndex(selectedList.getMousePosition(true));
617 List<?> draggedData = (List<?>) t.getTransferData(ACTION_FLAVOR);
618
619 Object leadItem = dropIndex >= 0 ? selected.elementAt(dropIndex) : null;
620 int dataLength = draggedData.size();
621
622
623 if (leadItem != null) {
624 for (Object o: draggedData) {
625 if (leadItem.equals(o))
626 return false;
627
628 }
629 }
630
631 int dragLeadIndex = -1;
632 boolean localDrop = "list".equals(movingComponent);
633
634 if (localDrop) {
635 dragLeadIndex = selected.indexOf(draggedData.get(0));
636 for (Object o: draggedData) {
637 selected.removeElement(o);
638 }
639 }
640 int[] indices = new int[dataLength];
641
642 if (localDrop) {
643 int adjustedLeadIndex = selected.indexOf(leadItem);
644 int insertionAdjustment = dragLeadIndex <= adjustedLeadIndex ? 1 : 0;
645 for (int i = 0; i < dataLength; i++) {
646 selected.insertElementAt(draggedData.get(i), adjustedLeadIndex + insertionAdjustment + i);
647 indices[i] = adjustedLeadIndex + insertionAdjustment + i;
648 }
649 } else {
650 for (int i = 0; i < dataLength; i++) {
651 selected.add(dropIndex, draggedData.get(i));
652 indices[i] = dropIndex + i;
653 }
654 }
655 selectedList.clearSelection();
656 selectedList.setSelectedIndices(indices);
657 movingComponent = "";
658 return true;
659 } catch (Exception e) {
660 e.printStackTrace();
661 }
662 return false;
663 }
664
665 @Override
666 protected void exportDone(JComponent source, Transferable data, int action) {
667 if (movingComponent.equals("list")) {
668 try {
669 List<?> draggedData = (List<?>) data.getTransferData(ACTION_FLAVOR);
670 boolean localDrop = selected.contains(draggedData.get(0));
671 if (localDrop) {
672 int[] indices = selectedList.getSelectedIndices();
673 Arrays.sort(indices);
674 for (int i = indices.length - 1; i >= 0; i--) {
675 selected.remove(indices[i]);
676 }
677 }
678 } catch (Exception e) {
679 e.printStackTrace();
680 }
681 movingComponent = "";
682 }
683 }
684 });
685
686 actionsTree.setTransferHandler(new TransferHandler() {
687 private static final long serialVersionUID = 1L;
688
689 @Override
690 public int getSourceActions( JComponent c ){
691 return TransferHandler.MOVE;
692 }
693
694 @Override
695 protected void exportDone(JComponent source, Transferable data, int action) {
696 }
697
698 @Override
699 protected Transferable createTransferable(JComponent c) {
700 TreePath[] paths = actionsTree.getSelectionPaths();
701 List<ActionDefinition> dragActions = new ArrayList<ActionDefinition>();
702 for (TreePath path : paths) {
703 DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
704 Object obj = node.getUserObject();
705 if (obj == null) {
706 dragActions.add(ActionDefinition.getSeparator());
707 }
708 else if (obj instanceof Action) {
709 dragActions.add(new ActionDefinition((Action) obj));
710 }
711 }
712 return new ActionTransferable(dragActions);
713 }
714 });
715 actionsTree.setDragEnabled(true);
716 actionsTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
717 public void valueChanged(TreeSelectionEvent e) {
718 updateEnabledState();
719 }
720 });
721
722 final JPanel left = new JPanel(new GridBagLayout());
723 left.add(new JLabel(tr("Toolbar")), GBC.eol());
724 left.add(new JScrollPane(selectedList), GBC.std().fill(GBC.BOTH));
725
726 final JPanel right = new JPanel(new GridBagLayout());
727 right.add(new JLabel(tr("Available")), GBC.eol());
728 right.add(new JScrollPane(actionsTree), GBC.eol().fill(GBC.BOTH));
729
730 final JPanel buttons = new JPanel(new GridLayout(6,1));
731 buttons.add(upButton = createButton("up"));
732 buttons.add(addButton = createButton("<"));
733 buttons.add(removeButton = createButton(">"));
734 buttons.add(downButton = createButton("down"));
735 updateEnabledState();
736
737 final JPanel p = new JPanel();
738 p.setLayout(new LayoutManager(){
739 public void addLayoutComponent(String name, Component comp) {}
740 public void removeLayoutComponent(Component comp) {}
741 public Dimension minimumLayoutSize(Container parent) {
742 Dimension l = left.getMinimumSize();
743 Dimension r = right.getMinimumSize();
744 Dimension b = buttons.getMinimumSize();
745 return new Dimension(l.width+b.width+10+r.width,l.height+b.height+10+r.height);
746 }
747 public Dimension preferredLayoutSize(Container parent) {
748 Dimension l = new Dimension(200, 200); //left.getPreferredSize();
749 Dimension r = new Dimension(200, 200); //right.getPreferredSize();
750 return new Dimension(l.width+r.width+10+buttons.getPreferredSize().width,Math.max(l.height, r.height));
751 }
752 public void layoutContainer(Container parent) {
753 Dimension d = p.getSize();
754 Dimension b = buttons.getPreferredSize();
755 int width = (d.width-10-b.width)/2;
756 left.setBounds(new Rectangle(0,0,width,d.height));
757 right.setBounds(new Rectangle(width+10+b.width,0,width,d.height));
758 buttons.setBounds(new Rectangle(width+5, d.height/2-b.height/2, b.width, b.height));
759 }
760 });
761 p.add(left);
762 p.add(buttons);
763 p.add(right);
764
765 actionParametersPanel = new JPanel(new GridBagLayout());
766 actionParametersPanel.add(new JLabel(tr("Action parameters")), GBC.eol().insets(0, 10, 0, 20));
767 actionParametersTable.getColumnModel().getColumn(0).setHeaderValue(tr("Parameter name"));
768 actionParametersTable.getColumnModel().getColumn(1).setHeaderValue(tr("Parameter value"));
769 actionParametersPanel.add(actionParametersTable.getTableHeader(), GBC.eol().fill(GBC.HORIZONTAL));
770 actionParametersPanel.add(actionParametersTable, GBC.eol().fill(GBC.BOTH).insets(0, 0, 0, 10));
771 actionParametersPanel.setVisible(false);
772
773 JPanel panel = gui.createPreferenceTab(this);
774 panel.add(p, GBC.eol().fill(GBC.BOTH));
775 panel.add(actionParametersPanel, GBC.eol().fill(GBC.HORIZONTAL));
776 selected.removeAllElements();
777 for (ActionDefinition actionDefinition: getDefinedActions()) {
778 selected.addElement(actionDefinition);
779 }
780 }
781
782 public boolean ok() {
783 Collection<String> t = new LinkedList<String>();
784 ActionParser parser = new ActionParser(null);
785 for (int i = 0; i < selected.size(); ++i) {
786 ActionDefinition action = (ActionDefinition)selected.get(i);
787 if (action.isSeparator()) {
788 t.add("|");
789 } else {
790 String res = parser.saveAction(action);
791 if(res != null)
792 t.add(res);
793 }
794 }
795 if (t.isEmpty()) {
796 t = Collections.singletonList(EMPTY_TOOLBAR_MARKER);
797 }
798 Main.pref.putCollection("toolbar", t);
799 Main.toolbar.refreshToolbarControl();
800 return false;
801 }
802
803 }
804
805 public ToolbarPreferences() {
806 control.setFloatable(false);
807 control.addMouseListener(new PopupMenuLauncher(new ToolbarPopupMenu(null)));
808 }
809
810 private void loadAction(DefaultMutableTreeNode node, MenuElement menu) {
811 Object userObject = null;
812 MenuElement menuElement = menu;
813 if (menu.getSubElements().length > 0 &&
814 menu.getSubElements()[0] instanceof JPopupMenu) {
815 menuElement = menu.getSubElements()[0];
816 }
817 for (MenuElement item : menuElement.getSubElements()) {
818 if (item instanceof JMenuItem) {
819 JMenuItem menuItem = ((JMenuItem)item);
820 if (menuItem.getAction() != null) {
821 Action action = menuItem.getAction();
822 userObject = action;
823 Object tb = action.getValue("toolbar");
824 if(tb == null) {
825 System.out.println(tr("Toolbar action without name: {0}",
826 action.getClass().getName()));
827 continue;
828 } else if (!(tb instanceof String)) {
829 if(!(tb instanceof Boolean) || (Boolean)tb) {
830 System.out.println(tr("Strange toolbar value: {0}",
831 action.getClass().getName()));
832 }
833 continue;
834 } else {
835 String toolbar = (String) tb;
836 Action r = actions.get(toolbar);
837 if(r != null && r != action && !toolbar.startsWith("imagery_")) {
838 System.out.println(tr("Toolbar action {0} overwritten: {1} gets {2}",
839 toolbar, r.getClass().getName(), action.getClass().getName()));
840 }
841 actions.put(toolbar, action);
842 }
843 } else {
844 userObject = menuItem.getText();
845 }
846 }
847 DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(userObject);
848 node.add(newNode);
849 loadAction(newNode, item);
850 }
851 }
852
853 public Action getAction(String s)
854 {
855 Action e = actions.get(s);
856 if(e == null) {
857 e = regactions.get(s);
858 }
859 return e;
860 }
861
862 private void loadActions() {
863 rootActionsNode.removeAllChildren();
864 loadAction(rootActionsNode, Main.main.menu);
865 for(Map.Entry<String, Action> a : regactions.entrySet())
866 {
867 if(actions.get(a.getKey()) == null) {
868 rootActionsNode.add(new DefaultMutableTreeNode(a.getValue()));
869 }
870 }
871 rootActionsNode.add(new DefaultMutableTreeNode(null));
872 }
873
874 private static final String[] deftoolbar = {"open", "save", "download", "upload", "|",
875 "undo", "redo", "|", "dialogs/search", "preference", "|", "splitway", "combineway",
876 "wayflip", "|", "imagery-offset", "|", "tagginggroup_Highways/Streets",
877 "tagginggroup_Highways/Ways", "tagginggroup_Highways/Waypoints",
878 "tagginggroup_Highways/Barriers", "|", "tagginggroup_Transport/Car",
879 "tagginggroup_Transport/Public Transport", "|", "tagginggroup_Facilities/Tourism",
880 "tagginggroup_Facilities/Food+Drinks", "|", "tagginggroup_Man Made/Historic Places", "|",
881 "tagginggroup_Man Made/Man Made"};
882
883 public static Collection<String> getToolString() {
884
885 Collection<String> toolStr = Main.pref.getCollection("toolbar", Arrays.asList(deftoolbar));
886 if (toolStr == null || toolStr.size() == 0) {
887 toolStr = Arrays.asList(deftoolbar);
888 }
889 return toolStr;
890 }
891
892 private Collection<ActionDefinition> getDefinedActions() {
893 loadActions();
894
895 Map<String, Action> allActions = new HashMap<String, Action>(regactions);
896 allActions.putAll(actions);
897 ActionParser actionParser = new ActionParser(allActions);
898
899 Collection<ActionDefinition> result = new ArrayList<ActionDefinition>();
900
901 for (String s : getToolString()) {
902 if (s.equals("|")) {
903 result.add(ActionDefinition.getSeparator());
904 } else {
905 ActionDefinition a = actionParser.loadAction(s);
906 if(a != null) {
907 result.add(a);
908 } else {
909 System.out.println("Could not load tool definition "+s);
910 }
911 }
912 }
913
914 return result;
915 }
916
917 /**
918 * @return The parameter (for better chaining)
919 */
920 public Action register(Action action) {
921 String toolbar = (String) action.getValue("toolbar");
922 if(toolbar == null) {
923 System.out.println(tr("Registered toolbar action without name: {0}",
924 action.getClass().getName()));
925 } else {
926 Action r = regactions.get(toolbar);
927 if(r != null) {
928 System.out.println(tr("Registered toolbar action {0} overwritten: {1} gets {2}",
929 toolbar, r.getClass().getName(), action.getClass().getName()));
930 }
931 }
932 regactions.put(toolbar, action);
933 return action;
934 }
935
936 /**
937 * Parse the toolbar preference setting and construct the toolbar GUI control.
938 *
939 * Call this, if anything has changed in the toolbar settings and you want to refresh
940 * the toolbar content (e.g. after registering actions in a plugin)
941 */
942 public void refreshToolbarControl() {
943 control.removeAll();
944
945 for (ActionDefinition action : getDefinedActions()) {
946 if (action.isSeparator()) {
947 control.addSeparator();
948 } else {
949 final JButton b = control.add(action.getParametrizedAction());
950 String tt = action.getDisplayTooltip();
951 if (tt != null && !tt.isEmpty())
952 b.setToolTipText(tt);
953 Icon i = action.getDisplayIcon();
954 if (i != null) {
955 b.setIcon(i);
956 } else {
957 // hide action text if an icon is set later (necessary for delayed/background image loading)
958 action.getParametrizedAction().addPropertyChangeListener(new PropertyChangeListener() {
959
960 @Override
961 public void propertyChange(PropertyChangeEvent evt) {
962 if (Action.SMALL_ICON.equals(evt.getPropertyName())) {
963 b.setHideActionText(evt.getNewValue() != null);
964 }
965 }
966 });
967 }
968 b.addMouseListener(new PopupMenuLauncher( new ToolbarPopupMenu(action)));
969 }
970 }
971 control.setVisible(control.getComponentCount() != 0);
972 }
973
974 private static DataFlavor ACTION_FLAVOR = new DataFlavor(
975 ActionDefinition.class, "ActionItem");
976
977 }