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 }