source: josm/trunk/src/org/openstreetmap/josm/gui/preferences/ToolbarPreferences.java@ 7020

Last change on this file since 7020 was 7020, checked in by Don-vip, 10 years ago

see #8465 - fix remaining compilation warnings

  • Property svn:eol-style set to native
File size: 44.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.preferences;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Component;
7import java.awt.Container;
8import java.awt.Dimension;
9import java.awt.GridBagLayout;
10import java.awt.GridLayout;
11import java.awt.LayoutManager;
12import java.awt.Rectangle;
13import java.awt.datatransfer.DataFlavor;
14import java.awt.datatransfer.Transferable;
15import java.awt.datatransfer.UnsupportedFlavorException;
16import java.awt.event.ActionEvent;
17import java.awt.event.ActionListener;
18import java.awt.event.InputEvent;
19import java.awt.event.KeyEvent;
20import java.beans.PropertyChangeEvent;
21import java.beans.PropertyChangeListener;
22import java.io.IOException;
23import java.util.ArrayList;
24import java.util.Arrays;
25import java.util.Collection;
26import java.util.Collections;
27import java.util.HashMap;
28import java.util.LinkedList;
29import java.util.List;
30import java.util.Map;
31
32import javax.swing.AbstractAction;
33import javax.swing.Action;
34import javax.swing.DefaultListCellRenderer;
35import javax.swing.DefaultListModel;
36import javax.swing.Icon;
37import javax.swing.ImageIcon;
38import javax.swing.JButton;
39import javax.swing.JCheckBoxMenuItem;
40import javax.swing.JComponent;
41import javax.swing.JLabel;
42import javax.swing.JList;
43import javax.swing.JMenuItem;
44import javax.swing.JPanel;
45import javax.swing.JPopupMenu;
46import javax.swing.JScrollPane;
47import javax.swing.JTable;
48import javax.swing.JToolBar;
49import javax.swing.JTree;
50import javax.swing.ListCellRenderer;
51import javax.swing.MenuElement;
52import javax.swing.TransferHandler;
53import javax.swing.event.ListSelectionEvent;
54import javax.swing.event.ListSelectionListener;
55import javax.swing.event.PopupMenuEvent;
56import javax.swing.event.PopupMenuListener;
57import javax.swing.event.TreeSelectionEvent;
58import javax.swing.event.TreeSelectionListener;
59import javax.swing.table.AbstractTableModel;
60import javax.swing.tree.DefaultMutableTreeNode;
61import javax.swing.tree.DefaultTreeCellRenderer;
62import javax.swing.tree.DefaultTreeModel;
63import javax.swing.tree.TreePath;
64
65import org.openstreetmap.josm.Main;
66import org.openstreetmap.josm.actions.ActionParameter;
67import org.openstreetmap.josm.actions.AdaptableAction;
68import org.openstreetmap.josm.actions.JosmAction;
69import org.openstreetmap.josm.actions.ParameterizedAction;
70import org.openstreetmap.josm.actions.ParameterizedActionDecorator;
71import org.openstreetmap.josm.gui.tagging.TaggingPreset;
72import org.openstreetmap.josm.tools.GBC;
73import org.openstreetmap.josm.tools.ImageProvider;
74import org.openstreetmap.josm.tools.Shortcut;
75
76public class ToolbarPreferences implements PreferenceSettingFactory {
77
78 private static final String EMPTY_TOOLBAR_MARKER = "<!-empty-!>";
79
80 public static class ActionDefinition {
81 private final Action action;
82 private String name = "";
83 private String icon = "";
84 private ImageIcon ico = null;
85 private final Map<String, Object> parameters = new HashMap<>();
86
87 public ActionDefinition(Action action) {
88 this.action = action;
89 }
90
91 public Map<String, Object> getParameters() {
92 return parameters;
93 }
94
95 public Action getParametrizedAction() {
96 if (getAction() instanceof ParameterizedAction)
97 return new ParameterizedActionDecorator((ParameterizedAction) getAction(), parameters);
98 else
99 return getAction();
100 }
101
102 public Action getAction() {
103 return action;
104 }
105
106 public String getName() {
107 return name;
108 }
109
110 public String getDisplayName() {
111 return name.isEmpty() ? (String) action.getValue(Action.NAME) : name;
112 }
113
114 public String getDisplayTooltip() {
115 if(!name.isEmpty())
116 return name;
117
118 Object tt = action.getValue(TaggingPreset.OPTIONAL_TOOLTIP_TEXT);
119 if (tt != null)
120 return (String) tt;
121
122 return (String) action.getValue(Action.SHORT_DESCRIPTION);
123 }
124
125 public Icon getDisplayIcon() {
126 return ico != null ? ico : (Icon) action.getValue(Action.SMALL_ICON);
127 }
128
129 public void setName(String name) {
130 this.name = name;
131 }
132
133 public String getIcon() {
134 return icon;
135 }
136
137 public void setIcon(String icon) {
138 this.icon = icon;
139 ico = ImageProvider.getIfAvailable("", icon);
140 }
141
142 public boolean isSeparator() {
143 return action == null;
144 }
145
146 public static ActionDefinition getSeparator() {
147 return new ActionDefinition(null);
148 }
149
150 public boolean hasParameters() {
151 if (!(getAction() instanceof ParameterizedAction)) return false;
152 for (Object o: parameters.values()) {
153 if (o!=null) return true;
154 }
155 return false;
156 }
157 }
158
159 public static class ActionParser {
160 private final Map<String, Action> actions;
161 private final StringBuilder result = new StringBuilder();
162 private int index;
163 private char[] s;
164
165 public ActionParser(Map<String, Action> actions) {
166 this.actions = actions;
167 }
168
169 private String readTillChar(char ch1, char ch2) {
170 result.setLength(0);
171 while (index < s.length && s[index] != ch1 && s[index] != ch2) {
172 if (s[index] == '\\') {
173 index++;
174 if (index >= s.length) {
175 break;
176 }
177 }
178 result.append(s[index]);
179 index++;
180 }
181 return result.toString();
182 }
183
184 private void skip(char ch) {
185 if (index < s.length && s[index] == ch) {
186 index++;
187 }
188 }
189
190 public ActionDefinition loadAction(String actionName) {
191 index = 0;
192 this.s = actionName.toCharArray();
193
194 String name = readTillChar('(', '{');
195 Action action = actions.get(name);
196
197 if (action == null)
198 return null;
199
200 ActionDefinition result = new ActionDefinition(action);
201
202 if (action instanceof ParameterizedAction) {
203 skip('(');
204
205 ParameterizedAction parametrizedAction = (ParameterizedAction)action;
206 Map<String, ActionParameter<?>> actionParams = new HashMap<>();
207 for (ActionParameter<?> param: parametrizedAction.getActionParameters()) {
208 actionParams.put(param.getName(), param);
209 }
210
211 while (index < s.length && s[index] != ')') {
212 String paramName = readTillChar('=', '=');
213 skip('=');
214 String paramValue = readTillChar(',',')');
215 if (paramName.length() > 0) {
216 ActionParameter<?> actionParam = actionParams.get(paramName);
217 if (actionParam != null) {
218 result.getParameters().put(paramName, actionParam.readFromString(paramValue));
219 }
220 }
221 skip(',');
222 }
223 skip(')');
224 }
225 if (action instanceof AdaptableAction) {
226 skip('{');
227
228 while (index < s.length && s[index] != '}') {
229 String paramName = readTillChar('=', '=');
230 skip('=');
231 String paramValue = readTillChar(',','}');
232 if ("icon".equals(paramName) && paramValue.length() > 0) {
233 result.setIcon(paramValue);
234 } else if("name".equals(paramName) && paramValue.length() > 0) {
235 result.setName(paramValue);
236 }
237 skip(',');
238 }
239 skip('}');
240 }
241
242 return result;
243 }
244
245 private void escape(String s) {
246 for (int i=0; i<s.length(); i++) {
247 char ch = s.charAt(i);
248 if (ch == '\\' || ch == '(' || ch == '{' || ch == ',' || ch == ')' || ch == '}' || ch == '=') {
249 result.append('\\');
250 result.append(ch);
251 } else {
252 result.append(ch);
253 }
254 }
255 }
256
257 @SuppressWarnings("unchecked")
258 public String saveAction(ActionDefinition action) {
259 result.setLength(0);
260
261 String val = (String) action.getAction().getValue("toolbar");
262 if(val == null)
263 return null;
264 escape(val);
265 if (action.getAction() instanceof ParameterizedAction) {
266 result.append('(');
267 List<ActionParameter<?>> params = ((ParameterizedAction)action.getAction()).getActionParameters();
268 for (int i=0; i<params.size(); i++) {
269 ActionParameter<Object> param = (ActionParameter<Object>)params.get(i);
270 escape(param.getName());
271 result.append('=');
272 Object value = action.getParameters().get(param.getName());
273 if (value != null) {
274 escape(param.writeToString(value));
275 }
276 if (i < params.size() - 1) {
277 result.append(',');
278 } else {
279 result.append(')');
280 }
281 }
282 }
283 if (action.getAction() instanceof AdaptableAction) {
284 boolean first = true;
285 String tmp = action.getName();
286 if(tmp.length() != 0) {
287 result.append(first ? "{" : ",");
288 result.append("name=");
289 escape(tmp);
290 first = false;
291 }
292 tmp = action.getIcon();
293 if(tmp.length() != 0) {
294 result.append(first ? "{" : ",");
295 result.append("icon=");
296 escape(tmp);
297 first = false;
298 }
299 if(!first) {
300 result.append('}');
301 }
302 }
303
304 return result.toString();
305 }
306 }
307
308 private static class ActionParametersTableModel extends AbstractTableModel {
309
310 private ActionDefinition currentAction = ActionDefinition.getSeparator();
311
312 @Override
313 public int getColumnCount() {
314 return 2;
315 }
316
317 @Override
318 public int getRowCount() {
319 int adaptable = ((currentAction.getAction() instanceof AdaptableAction) ? 2 : 0);
320 if (currentAction.isSeparator() || !(currentAction.getAction() instanceof ParameterizedAction))
321 return adaptable;
322 ParameterizedAction pa = (ParameterizedAction)currentAction.getAction();
323 return pa.getActionParameters().size() + adaptable;
324 }
325
326 @SuppressWarnings("unchecked")
327 private ActionParameter<Object> getParam(int index) {
328 ParameterizedAction pa = (ParameterizedAction)currentAction.getAction();
329 return (ActionParameter<Object>) pa.getActionParameters().get(index);
330 }
331
332 @Override
333 public Object getValueAt(int rowIndex, int columnIndex) {
334 if(currentAction.getAction() instanceof AdaptableAction)
335 {
336 if (rowIndex < 2) {
337 switch (columnIndex) {
338 case 0:
339 return rowIndex == 0 ? tr("Tooltip") : tr("Icon");
340 case 1:
341 return rowIndex == 0 ? currentAction.getName() : currentAction.getIcon();
342 default:
343 return null;
344 }
345 } else {
346 rowIndex -= 2;
347 }
348 }
349 ActionParameter<Object> param = getParam(rowIndex);
350 switch (columnIndex) {
351 case 0:
352 return param.getName();
353 case 1:
354 return param.writeToString(currentAction.getParameters().get(param.getName()));
355 default:
356 return null;
357 }
358 }
359
360 @Override
361 public boolean isCellEditable(int row, int column) {
362 return column == 1;
363 }
364
365 @Override
366 public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
367 if(currentAction.getAction() instanceof AdaptableAction)
368 {
369 if (rowIndex == 0) {
370 currentAction.setName((String)aValue);
371 return;
372 } else if (rowIndex == 1) {
373 currentAction.setIcon((String)aValue);
374 return;
375 } else {
376 rowIndex -= 2;
377 }
378 }
379 ActionParameter<Object> param = getParam(rowIndex);
380 currentAction.getParameters().put(param.getName(), param.readFromString((String)aValue));
381 }
382
383 public void setCurrentAction(ActionDefinition currentAction) {
384 this.currentAction = currentAction;
385 fireTableDataChanged();
386 }
387 }
388
389 private class ToolbarPopupMenu extends JPopupMenu {
390 ActionDefinition act;
391
392 private void setActionAndAdapt(ActionDefinition action) {
393 this.act = action;
394 doNotHide.setSelected(Main.pref.getBoolean("toolbar.always-visible", true));
395 remove.setVisible(act!=null);
396 shortcutEdit.setVisible(act!=null);
397 }
398
399 JMenuItem remove = new JMenuItem(new AbstractAction(tr("Remove from toolbar")) {
400 @Override
401 public void actionPerformed(ActionEvent e) {
402 Collection<String> t = new LinkedList<>(getToolString());
403 ActionParser parser = new ActionParser(null);
404 // get text definition of current action
405 String res = parser.saveAction(act);
406 // remove the button from toolbar preferences
407 t.remove( res );
408 Main.pref.putCollection("toolbar", t);
409 Main.toolbar.refreshToolbarControl();
410 }
411 });
412
413 JMenuItem configure = new JMenuItem(new AbstractAction(tr("Configure toolbar")) {
414 @Override
415 public void actionPerformed(ActionEvent e) {
416 final PreferenceDialog p =new PreferenceDialog(Main.parent);
417 p.selectPreferencesTabByName("toolbar");
418 p.setVisible(true);
419 }
420 });
421
422 JMenuItem shortcutEdit = new JMenuItem(new AbstractAction(tr("Edit shortcut")) {
423 @Override
424 public void actionPerformed(ActionEvent e) {
425 final PreferenceDialog p =new PreferenceDialog(Main.parent);
426 p.getTabbedPane().getShortcutPreference().setDefaultFilter(act.getDisplayName());
427 p.selectPreferencesTabByName("shortcuts");
428 p.setVisible(true);
429 // refresh toolbar to try using changed shortcuts without restart
430 Main.toolbar.refreshToolbarControl();
431 }
432 });
433
434 JCheckBoxMenuItem doNotHide = new JCheckBoxMenuItem(new AbstractAction(tr("Do not hide toolbar and menu")) {
435 @Override
436 public void actionPerformed(ActionEvent e) {
437 boolean sel = ((JCheckBoxMenuItem) e.getSource()).getState();
438 Main.pref.put("toolbar.always-visible", sel);
439 Main.pref.put("menu.always-visible", sel);
440 }
441 });
442 {
443 addPopupMenuListener(new PopupMenuListener() {
444 @Override
445 public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
446 setActionAndAdapt(buttonActions.get(
447 ((JPopupMenu)e.getSource()).getInvoker()
448 ));
449 }
450 @Override
451 public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {}
452
453 @Override
454 public void popupMenuCanceled(PopupMenuEvent e) {}
455 });
456 add(remove);
457 add(configure);
458 add(shortcutEdit);
459 add(doNotHide);
460 }
461 }
462
463 private ToolbarPopupMenu popupMenu = new ToolbarPopupMenu();
464
465 /**
466 * Key: Registered name (property "toolbar" of action).
467 * Value: The action to execute.
468 */
469 private final Map<String, Action> actions = new HashMap<>();
470 private final Map<String, Action> regactions = new HashMap<>();
471
472 private final DefaultMutableTreeNode rootActionsNode = new DefaultMutableTreeNode(tr("Actions"));
473
474 public JToolBar control = new JToolBar();
475 private final Map<Object, ActionDefinition> buttonActions = new HashMap<>(30);
476
477 @Override
478 public PreferenceSetting createPreferenceSetting() {
479 return new Settings(rootActionsNode);
480 }
481
482 public class Settings extends DefaultTabPreferenceSetting {
483
484 private final class Move implements ActionListener {
485 @Override
486 public void actionPerformed(ActionEvent e) {
487 if ("<".equals(e.getActionCommand()) && actionsTree.getSelectionCount() > 0) {
488
489 int leadItem = selected.getSize();
490 if (selectedList.getSelectedIndex() != -1) {
491 int[] indices = selectedList.getSelectedIndices();
492 leadItem = indices[indices.length - 1];
493 }
494 for (TreePath selectedAction : actionsTree.getSelectionPaths()) {
495 DefaultMutableTreeNode node = (DefaultMutableTreeNode) selectedAction.getLastPathComponent();
496 if (node.getUserObject() == null) {
497 selected.add(leadItem++, ActionDefinition.getSeparator());
498 } else if (node.getUserObject() instanceof Action) {
499 selected.add(leadItem++, new ActionDefinition((Action)node.getUserObject()));
500 }
501 }
502 } else if (">".equals(e.getActionCommand()) && selectedList.getSelectedIndex() != -1) {
503 while (selectedList.getSelectedIndex() != -1) {
504 selected.remove(selectedList.getSelectedIndex());
505 }
506 } else if ("up".equals(e.getActionCommand())) {
507 int i = selectedList.getSelectedIndex();
508 ActionDefinition o = selected.get(i);
509 if (i != 0) {
510 selected.remove(i);
511 selected.add(i-1, o);
512 selectedList.setSelectedIndex(i-1);
513 }
514 } else if ("down".equals(e.getActionCommand())) {
515 int i = selectedList.getSelectedIndex();
516 ActionDefinition o = selected.get(i);
517 if (i != selected.size()-1) {
518 selected.remove(i);
519 selected.add(i+1, o);
520 selectedList.setSelectedIndex(i+1);
521 }
522 }
523 }
524 }
525
526 private class ActionTransferable implements Transferable {
527
528 private final DataFlavor[] flavors = new DataFlavor[] { ACTION_FLAVOR };
529
530 private final List<ActionDefinition> actions;
531
532 public ActionTransferable(List<ActionDefinition> actions) {
533 this.actions = actions;
534 }
535
536 @Override
537 public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
538 return actions;
539 }
540
541 @Override
542 public DataFlavor[] getTransferDataFlavors() {
543 return flavors;
544 }
545
546 @Override
547 public boolean isDataFlavorSupported(DataFlavor flavor) {
548 return flavors[0] == flavor;
549 }
550 }
551
552 private final Move moveAction = new Move();
553
554 private final DefaultListModel<ActionDefinition> selected = new DefaultListModel<>();
555 private final JList<ActionDefinition> selectedList = new JList<>(selected);
556
557 private final DefaultTreeModel actionsTreeModel;
558 private final JTree actionsTree;
559
560 private final ActionParametersTableModel actionParametersModel = new ActionParametersTableModel();
561 private final JTable actionParametersTable = new JTable(actionParametersModel);
562 private JPanel actionParametersPanel;
563
564 private JButton upButton;
565 private JButton downButton;
566 private JButton removeButton;
567 private JButton addButton;
568
569 private String movingComponent;
570
571 public Settings(DefaultMutableTreeNode rootActionsNode) {
572 super("toolbar", tr("Toolbar customization"), tr("Customize the elements on the toolbar."));
573 actionsTreeModel = new DefaultTreeModel(rootActionsNode);
574 actionsTree = new JTree(actionsTreeModel);
575 }
576
577 private JButton createButton(String name) {
578 JButton b = new JButton();
579 if ("up".equals(name)) {
580 b.setIcon(ImageProvider.get("dialogs", "up"));
581 } else if ("down".equals(name)) {
582 b.setIcon(ImageProvider.get("dialogs", "down"));
583 } else {
584 b.setText(name);
585 }
586 b.addActionListener(moveAction);
587 b.setActionCommand(name);
588 return b;
589 }
590
591 private void updateEnabledState() {
592 int index = selectedList.getSelectedIndex();
593 upButton.setEnabled(index > 0);
594 downButton.setEnabled(index != -1 && index < selectedList.getModel().getSize() - 1);
595 removeButton.setEnabled(index != -1);
596 addButton.setEnabled(actionsTree.getSelectionCount() > 0);
597 }
598
599 @Override
600 public void addGui(PreferenceTabbedPane gui) {
601 actionsTree.setCellRenderer(new DefaultTreeCellRenderer() {
602 @Override
603 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
604 boolean leaf, int row, boolean hasFocus) {
605 DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
606 JLabel comp = (JLabel) super.getTreeCellRendererComponent(
607 tree, value, sel, expanded, leaf, row, hasFocus);
608 if (node.getUserObject() == null) {
609 comp.setText(tr("Separator"));
610 comp.setIcon(ImageProvider.get("preferences/separator"));
611 }
612 else if (node.getUserObject() instanceof Action) {
613 Action action = (Action) node.getUserObject();
614 comp.setText((String) action.getValue(Action.NAME));
615 comp.setIcon((Icon) action.getValue(Action.SMALL_ICON));
616 }
617 return comp;
618 }
619 });
620
621 ListCellRenderer<ActionDefinition> renderer = new ListCellRenderer<ActionDefinition>() {
622 final DefaultListCellRenderer def = new DefaultListCellRenderer();
623 @Override
624 public Component getListCellRendererComponent(JList<? extends ActionDefinition> list,
625 ActionDefinition value, int index, boolean isSelected, boolean cellHasFocus) {
626 String s;
627 Icon i;
628 ActionDefinition action = (ActionDefinition)value;
629 if (!action.isSeparator()) {
630 s = action.getDisplayName();
631 i = action.getDisplayIcon();
632 } else {
633 i = ImageProvider.get("preferences/separator");
634 s = tr("Separator");
635 }
636 JLabel l = (JLabel)def.getListCellRendererComponent(list, s, index, isSelected, cellHasFocus);
637 l.setIcon(i);
638 return l;
639 }
640 };
641 selectedList.setCellRenderer(renderer);
642 selectedList.addListSelectionListener(new ListSelectionListener(){
643 @Override
644 public void valueChanged(ListSelectionEvent e) {
645 boolean sel = selectedList.getSelectedIndex() != -1;
646 if (sel) {
647 actionsTree.clearSelection();
648 ActionDefinition action = (ActionDefinition) selected.get(selectedList.getSelectedIndex());
649 actionParametersModel.setCurrentAction(action);
650 actionParametersPanel.setVisible(actionParametersModel.getRowCount() > 0);
651 }
652 updateEnabledState();
653 }
654 });
655
656 selectedList.setDragEnabled(true);
657 selectedList.setTransferHandler(new TransferHandler() {
658 @Override
659 @SuppressWarnings("unchecked")
660 protected Transferable createTransferable(JComponent c) {
661 List<ActionDefinition> actions = new ArrayList<>();
662 for (ActionDefinition o: ((JList<ActionDefinition>)c).getSelectedValuesList()) {
663 actions.add(o);
664 }
665 return new ActionTransferable(actions);
666 }
667
668 @Override
669 public int getSourceActions(JComponent c) {
670 return TransferHandler.MOVE;
671 }
672
673 @Override
674 public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
675 for (DataFlavor f : transferFlavors) {
676 if (ACTION_FLAVOR.equals(f))
677 return true;
678 }
679 return false;
680 }
681
682 @Override
683 public void exportAsDrag(JComponent comp, InputEvent e, int action) {
684 super.exportAsDrag(comp, e, action);
685 movingComponent = "list";
686 }
687
688 @Override
689 public boolean importData(JComponent comp, Transferable t) {
690 try {
691 int dropIndex = selectedList.locationToIndex(selectedList.getMousePosition(true));
692 @SuppressWarnings("unchecked")
693 List<ActionDefinition> draggedData = (List<ActionDefinition>) t.getTransferData(ACTION_FLAVOR);
694
695 Object leadItem = dropIndex >= 0 ? selected.elementAt(dropIndex) : null;
696 int dataLength = draggedData.size();
697
698 if (leadItem != null) {
699 for (Object o: draggedData) {
700 if (leadItem.equals(o))
701 return false;
702 }
703 }
704
705 int dragLeadIndex = -1;
706 boolean localDrop = "list".equals(movingComponent);
707
708 if (localDrop) {
709 dragLeadIndex = selected.indexOf(draggedData.get(0));
710 for (Object o: draggedData) {
711 selected.removeElement(o);
712 }
713 }
714 int[] indices = new int[dataLength];
715
716 if (localDrop) {
717 int adjustedLeadIndex = selected.indexOf(leadItem);
718 int insertionAdjustment = dragLeadIndex <= adjustedLeadIndex ? 1 : 0;
719 for (int i = 0; i < dataLength; i++) {
720 selected.insertElementAt(draggedData.get(i), adjustedLeadIndex + insertionAdjustment + i);
721 indices[i] = adjustedLeadIndex + insertionAdjustment + i;
722 }
723 } else {
724 for (int i = 0; i < dataLength; i++) {
725 selected.add(dropIndex, draggedData.get(i));
726 indices[i] = dropIndex + i;
727 }
728 }
729 selectedList.clearSelection();
730 selectedList.setSelectedIndices(indices);
731 movingComponent = "";
732 return true;
733 } catch (Exception e) {
734 Main.error(e);
735 }
736 return false;
737 }
738
739 @Override
740 protected void exportDone(JComponent source, Transferable data, int action) {
741 if ("list".equals(movingComponent)) {
742 try {
743 List<?> draggedData = (List<?>) data.getTransferData(ACTION_FLAVOR);
744 boolean localDrop = selected.contains(draggedData.get(0));
745 if (localDrop) {
746 int[] indices = selectedList.getSelectedIndices();
747 Arrays.sort(indices);
748 for (int i = indices.length - 1; i >= 0; i--) {
749 selected.remove(indices[i]);
750 }
751 }
752 } catch (Exception e) {
753 Main.error(e);
754 }
755 movingComponent = "";
756 }
757 }
758 });
759
760 actionsTree.setTransferHandler(new TransferHandler() {
761 private static final long serialVersionUID = 1L;
762
763 @Override
764 public int getSourceActions( JComponent c ){
765 return TransferHandler.MOVE;
766 }
767
768 @Override
769 protected void exportDone(JComponent source, Transferable data, int action) {
770 }
771
772 @Override
773 protected Transferable createTransferable(JComponent c) {
774 TreePath[] paths = actionsTree.getSelectionPaths();
775 List<ActionDefinition> dragActions = new ArrayList<>();
776 for (TreePath path : paths) {
777 DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
778 Object obj = node.getUserObject();
779 if (obj == null) {
780 dragActions.add(ActionDefinition.getSeparator());
781 }
782 else if (obj instanceof Action) {
783 dragActions.add(new ActionDefinition((Action) obj));
784 }
785 }
786 return new ActionTransferable(dragActions);
787 }
788 });
789 actionsTree.setDragEnabled(true);
790 actionsTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
791 @Override public void valueChanged(TreeSelectionEvent e) {
792 updateEnabledState();
793 }
794 });
795
796 final JPanel left = new JPanel(new GridBagLayout());
797 left.add(new JLabel(tr("Toolbar")), GBC.eol());
798 left.add(new JScrollPane(selectedList), GBC.std().fill(GBC.BOTH));
799
800 final JPanel right = new JPanel(new GridBagLayout());
801 right.add(new JLabel(tr("Available")), GBC.eol());
802 right.add(new JScrollPane(actionsTree), GBC.eol().fill(GBC.BOTH));
803
804 final JPanel buttons = new JPanel(new GridLayout(6,1));
805 buttons.add(upButton = createButton("up"));
806 buttons.add(addButton = createButton("<"));
807 buttons.add(removeButton = createButton(">"));
808 buttons.add(downButton = createButton("down"));
809 updateEnabledState();
810
811 final JPanel p = new JPanel();
812 p.setLayout(new LayoutManager(){
813 @Override
814 public void addLayoutComponent(String name, Component comp) {}
815 @Override
816 public void removeLayoutComponent(Component comp) {}
817 @Override
818 public Dimension minimumLayoutSize(Container parent) {
819 Dimension l = left.getMinimumSize();
820 Dimension r = right.getMinimumSize();
821 Dimension b = buttons.getMinimumSize();
822 return new Dimension(l.width+b.width+10+r.width,l.height+b.height+10+r.height);
823 }
824 @Override
825 public Dimension preferredLayoutSize(Container parent) {
826 Dimension l = new Dimension(200, 200);
827 Dimension r = new Dimension(200, 200);
828 return new Dimension(l.width+r.width+10+buttons.getPreferredSize().width,Math.max(l.height, r.height));
829 }
830 @Override
831 public void layoutContainer(Container parent) {
832 Dimension d = p.getSize();
833 Dimension b = buttons.getPreferredSize();
834 int width = (d.width-10-b.width)/2;
835 left.setBounds(new Rectangle(0,0,width,d.height));
836 right.setBounds(new Rectangle(width+10+b.width,0,width,d.height));
837 buttons.setBounds(new Rectangle(width+5, d.height/2-b.height/2, b.width, b.height));
838 }
839 });
840 p.add(left);
841 p.add(buttons);
842 p.add(right);
843
844 actionParametersPanel = new JPanel(new GridBagLayout());
845 actionParametersPanel.add(new JLabel(tr("Action parameters")), GBC.eol().insets(0, 10, 0, 20));
846 actionParametersTable.getColumnModel().getColumn(0).setHeaderValue(tr("Parameter name"));
847 actionParametersTable.getColumnModel().getColumn(1).setHeaderValue(tr("Parameter value"));
848 actionParametersPanel.add(actionParametersTable.getTableHeader(), GBC.eol().fill(GBC.HORIZONTAL));
849 actionParametersPanel.add(actionParametersTable, GBC.eol().fill(GBC.BOTH).insets(0, 0, 0, 10));
850 actionParametersPanel.setVisible(false);
851
852 JPanel panel = gui.createPreferenceTab(this);
853 panel.add(p, GBC.eol().fill(GBC.BOTH));
854 panel.add(actionParametersPanel, GBC.eol().fill(GBC.HORIZONTAL));
855 selected.removeAllElements();
856 for (ActionDefinition actionDefinition: getDefinedActions()) {
857 selected.addElement(actionDefinition);
858 }
859 }
860
861 @Override
862 public boolean ok() {
863 Collection<String> t = new LinkedList<>();
864 ActionParser parser = new ActionParser(null);
865 for (int i = 0; i < selected.size(); ++i) {
866 ActionDefinition action = (ActionDefinition)selected.get(i);
867 if (action.isSeparator()) {
868 t.add("|");
869 } else {
870 String res = parser.saveAction(action);
871 if(res != null) {
872 t.add(res);
873 }
874 }
875 }
876 if (t.isEmpty()) {
877 t = Collections.singletonList(EMPTY_TOOLBAR_MARKER);
878 }
879 Main.pref.putCollection("toolbar", t);
880 Main.toolbar.refreshToolbarControl();
881 return false;
882 }
883
884 }
885
886 /**
887 * Constructs a new {@code ToolbarPreferences}.
888 */
889 public ToolbarPreferences() {
890 control.setFloatable(false);
891 control.setComponentPopupMenu(popupMenu);
892 }
893
894 private void loadAction(DefaultMutableTreeNode node, MenuElement menu) {
895 Object userObject = null;
896 MenuElement menuElement = menu;
897 if (menu.getSubElements().length > 0 &&
898 menu.getSubElements()[0] instanceof JPopupMenu) {
899 menuElement = menu.getSubElements()[0];
900 }
901 for (MenuElement item : menuElement.getSubElements()) {
902 if (item instanceof JMenuItem) {
903 JMenuItem menuItem = ((JMenuItem)item);
904 if (menuItem.getAction() != null) {
905 Action action = menuItem.getAction();
906 userObject = action;
907 Object tb = action.getValue("toolbar");
908 if(tb == null) {
909 Main.info(tr("Toolbar action without name: {0}",
910 action.getClass().getName()));
911 continue;
912 } else if (!(tb instanceof String)) {
913 if(!(tb instanceof Boolean) || (Boolean)tb) {
914 Main.info(tr("Strange toolbar value: {0}",
915 action.getClass().getName()));
916 }
917 continue;
918 } else {
919 String toolbar = (String) tb;
920 Action r = actions.get(toolbar);
921 if(r != null && r != action && !toolbar.startsWith("imagery_")) {
922 Main.info(tr("Toolbar action {0} overwritten: {1} gets {2}",
923 toolbar, r.getClass().getName(), action.getClass().getName()));
924 }
925 actions.put(toolbar, action);
926 }
927 } else {
928 userObject = menuItem.getText();
929 }
930 }
931 DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(userObject);
932 node.add(newNode);
933 loadAction(newNode, item);
934 }
935 }
936
937 public Action getAction(String s) {
938 Action e = actions.get(s);
939 if(e == null) {
940 e = regactions.get(s);
941 }
942 return e;
943 }
944
945 private void loadActions() {
946 rootActionsNode.removeAllChildren();
947 loadAction(rootActionsNode, Main.main.menu);
948 for(Map.Entry<String, Action> a : regactions.entrySet())
949 {
950 if(actions.get(a.getKey()) == null) {
951 rootActionsNode.add(new DefaultMutableTreeNode(a.getValue()));
952 }
953 }
954 rootActionsNode.add(new DefaultMutableTreeNode(null));
955 }
956
957 private static final String[] deftoolbar = {"open", "save", "download", "upload", "|",
958 "undo", "redo", "|", "dialogs/search", "preference", "|", "splitway", "combineway",
959 "wayflip", "|", "imagery-offset", "|", "tagginggroup_Highways/Streets",
960 "tagginggroup_Highways/Ways", "tagginggroup_Highways/Waypoints",
961 "tagginggroup_Highways/Barriers", "|", "tagginggroup_Transport/Car",
962 "tagginggroup_Transport/Public Transport", "|", "tagginggroup_Facilities/Tourism",
963 "tagginggroup_Facilities/Food+Drinks", "|", "tagginggroup_Man Made/Historic Places", "|",
964 "tagginggroup_Man Made/Man Made"};
965
966 public static Collection<String> getToolString() {
967
968 Collection<String> toolStr = Main.pref.getCollection("toolbar", Arrays.asList(deftoolbar));
969 if (toolStr == null || toolStr.isEmpty()) {
970 toolStr = Arrays.asList(deftoolbar);
971 }
972 return toolStr;
973 }
974
975 private Collection<ActionDefinition> getDefinedActions() {
976 loadActions();
977
978 Map<String, Action> allActions = new HashMap<>(regactions);
979 allActions.putAll(actions);
980 ActionParser actionParser = new ActionParser(allActions);
981
982 Collection<ActionDefinition> result = new ArrayList<>();
983
984 for (String s : getToolString()) {
985 if ("|".equals(s)) {
986 result.add(ActionDefinition.getSeparator());
987 } else {
988 ActionDefinition a = actionParser.loadAction(s);
989 if(a != null) {
990 result.add(a);
991 } else {
992 Main.info("Could not load tool definition "+s);
993 }
994 }
995 }
996
997 return result;
998 }
999
1000 /**
1001 * @return The parameter (for better chaining)
1002 */
1003 public Action register(Action action) {
1004 String toolbar = (String) action.getValue("toolbar");
1005 if (toolbar == null) {
1006 Main.info(tr("Registered toolbar action without name: {0}",
1007 action.getClass().getName()));
1008 } else {
1009 Action r = regactions.get(toolbar);
1010 if (r != null) {
1011 Main.info(tr("Registered toolbar action {0} overwritten: {1} gets {2}",
1012 toolbar, r.getClass().getName(), action.getClass().getName()));
1013 }
1014 }
1015 regactions.put(toolbar, action);
1016 return action;
1017 }
1018
1019 /**
1020 * Parse the toolbar preference setting and construct the toolbar GUI control.
1021 *
1022 * Call this, if anything has changed in the toolbar settings and you want to refresh
1023 * the toolbar content (e.g. after registering actions in a plugin)
1024 */
1025 public void refreshToolbarControl() {
1026 control.removeAll();
1027 buttonActions.clear();
1028 boolean unregisterTab = Shortcut.findShortcut(KeyEvent.VK_TAB, 0)!=null;
1029
1030 for (ActionDefinition action : getDefinedActions()) {
1031 if (action.isSeparator()) {
1032 control.addSeparator();
1033 } else {
1034 final JButton b = addButtonAndShortcut(action);
1035 buttonActions.put(b, action);
1036
1037 Icon i = action.getDisplayIcon();
1038 if (i != null) {
1039 b.setIcon(i);
1040 } else {
1041 // hide action text if an icon is set later (necessary for delayed/background image loading)
1042 action.getParametrizedAction().addPropertyChangeListener(new PropertyChangeListener() {
1043
1044 @Override
1045 public void propertyChange(PropertyChangeEvent evt) {
1046 if (Action.SMALL_ICON.equals(evt.getPropertyName())) {
1047 b.setHideActionText(evt.getNewValue() != null);
1048 }
1049 }
1050 });
1051 }
1052 b.setInheritsPopupMenu(true);
1053 b.setFocusTraversalKeysEnabled(!unregisterTab);
1054 }
1055 }
1056 control.setFocusTraversalKeysEnabled(!unregisterTab);
1057 control.setVisible(control.getComponentCount() != 0);
1058 control.repaint();
1059 }
1060
1061 /**
1062 * The method to add custom button on toolbar like search or preset buttons
1063 * @param definitionText toolbar definition text to describe the new button,
1064 * must be carefully generated by using {@link ActionParser}
1065 * @param preferredIndex place to put the new button, give -1 for the end of toolbar
1066 * @param removeIfExists if true and the button already exists, remove it
1067 */
1068 public void addCustomButton(String definitionText, int preferredIndex, boolean removeIfExists) {
1069 LinkedList<String> t = new LinkedList<>(getToolString());
1070 if (t.contains(definitionText)) {
1071 if (!removeIfExists) return; // do nothing
1072 t.remove(definitionText);
1073 } else {
1074 if (preferredIndex>=0 && preferredIndex < t.size()) {
1075 t.add(preferredIndex, definitionText); // add to specified place
1076 } else {
1077 t.add(definitionText); // add to the end
1078 }
1079 }
1080 Main.pref.putCollection("toolbar", t);
1081 Main.toolbar.refreshToolbarControl();
1082 }
1083
1084 private JButton addButtonAndShortcut(ActionDefinition action) {
1085 Action act = action.getParametrizedAction();
1086 JButton b = control.add(act);
1087
1088 Shortcut sc = null;
1089 if (action.getAction() instanceof JosmAction) {
1090 sc = ((JosmAction) action.getAction()).getShortcut();
1091 if (sc.getAssignedKey() == KeyEvent.CHAR_UNDEFINED) {
1092 sc = null;
1093 }
1094 }
1095
1096 long paramCode = 0;
1097 if (action.hasParameters()) {
1098 paramCode = action.parameters.hashCode();
1099 }
1100
1101 String tt = action.getDisplayTooltip();
1102 if (tt==null) {
1103 tt="";
1104 }
1105
1106 if (sc == null || paramCode != 0) {
1107 String name = (String) action.getAction().getValue("toolbar");
1108 if (name==null) {
1109 name=action.getDisplayName();
1110 }
1111 if (paramCode!=0) {
1112 name = name+paramCode;
1113 }
1114 String desc = action.getDisplayName() + ((paramCode==0)?"":action.parameters.toString());
1115 sc = Shortcut.registerShortcut("toolbar:"+name, tr("Toolbar: {0}", desc),
1116 KeyEvent.CHAR_UNDEFINED, Shortcut.NONE);
1117 Main.unregisterShortcut(sc);
1118 Main.registerActionShortcut(act, sc);
1119
1120 // add shortcut info to the tooltip if needed
1121 if (sc.getAssignedUser()) {
1122 if (tt.startsWith("<html>") && tt.endsWith("</html>")) {
1123 tt = tt.substring(6,tt.length()-6);
1124 }
1125 tt = Main.platform.makeTooltip(tt, sc);
1126 }
1127 }
1128
1129 if (!tt.isEmpty()) {
1130 b.setToolTipText(tt);
1131 }
1132 return b;
1133 }
1134
1135 private static final DataFlavor ACTION_FLAVOR = new DataFlavor(ActionDefinition.class, "ActionItem");
1136}
Note: See TracBrowser for help on using the repository browser.