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

Last change on this file since 1415 was 1393, checked in by stoecker, 15 years ago

fix #2156

  • Property svn:eol-style set to native
File size: 19.1 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
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.io.IOException;
20import java.util.Arrays;
21import java.util.Collection;
22import java.util.HashMap;
23import java.util.LinkedList;
24import java.util.List;
25import java.util.Map;
26
27import javax.swing.AbstractAction;
28import javax.swing.Action;
29import javax.swing.DefaultListCellRenderer;
30import javax.swing.DefaultListModel;
31import javax.swing.Icon;
32import javax.swing.JButton;
33import javax.swing.JComponent;
34import javax.swing.JLabel;
35import javax.swing.JList;
36import javax.swing.JMenuItem;
37import javax.swing.JPanel;
38import javax.swing.JPopupMenu;
39import javax.swing.JScrollPane;
40import javax.swing.JToolBar;
41import javax.swing.JTree;
42import javax.swing.ListCellRenderer;
43import javax.swing.MenuElement;
44import javax.swing.TransferHandler;
45import javax.swing.event.ListSelectionEvent;
46import javax.swing.event.ListSelectionListener;
47import javax.swing.tree.DefaultMutableTreeNode;
48import javax.swing.tree.DefaultTreeCellRenderer;
49import javax.swing.tree.DefaultTreeModel;
50import javax.swing.tree.TreePath;
51
52import org.openstreetmap.josm.Main;
53import org.openstreetmap.josm.tools.GBC;
54import org.openstreetmap.josm.tools.ImageProvider;
55
56public class ToolbarPreferences implements PreferenceSetting {
57
58 private final class Move implements ActionListener {
59 public void actionPerformed(ActionEvent e) {
60 if (e.getActionCommand().equals("<") && actionsTree.getSelectionCount() > 0) {
61
62 int leadItem = selected.getSize();
63 if (selectedList.getSelectedIndex() != -1) {
64 int[] indices = selectedList.getSelectedIndices();
65 leadItem = indices[indices.length - 1];
66 }
67 for (TreePath selectedAction : actionsTree.getSelectionPaths()) {
68 DefaultMutableTreeNode node = (DefaultMutableTreeNode) selectedAction.getLastPathComponent();
69 if (node.getUserObject() == null || node.getUserObject() instanceof Action) {
70 selected.add(leadItem++, ((Action)node.getUserObject()).getValue("toolbar"));
71 }
72 }
73 } else if (e.getActionCommand().equals(">") && selectedList.getSelectedIndex() != -1) {
74 while (selectedList.getSelectedIndex() != -1) {
75 selected.remove(selectedList.getSelectedIndex());
76 }
77 } else if (e.getActionCommand().equals("up")) {
78 int i = selectedList.getSelectedIndex();
79 Object o = selected.get(i);
80 if (i != 0) {
81 selected.remove(i);
82 selected.add(i-1, o);
83 selectedList.setSelectedIndex(i-1);
84 }
85 } else if (e.getActionCommand().equals("down")) {
86 int i = selectedList.getSelectedIndex();
87 Object o = selected.get(i);
88 if (i != selected.size()-1) {
89 selected.remove(i);
90 selected.add(i+1, o);
91 selectedList.setSelectedIndex(i+1);
92 }
93 }
94 }
95 }
96 private Move moveAction = new Move();
97
98 /**
99 * Key: Registered name (property "toolbar" of action).
100 * Value: The action to execute.
101 */
102 private Map<String, Action> actions = new HashMap<String, Action>();
103 private Map<String, Action> regactions = new HashMap<String, Action>();
104
105 private DefaultListModel selected = new DefaultListModel();
106
107 private DefaultMutableTreeNode rootActionsNode = new DefaultMutableTreeNode("Actions");
108 private DefaultTreeModel actionsTreeModel = new DefaultTreeModel(rootActionsNode);
109 private JTree actionsTree = new JTree(actionsTreeModel);
110 private JList selectedList = new JList(selected);
111
112 private String movingComponent;
113
114 public JToolBar control = new JToolBar();
115
116 private JButton upButton;
117 private JButton downButton;
118
119 public ToolbarPreferences() {
120 control.setFloatable(false);
121
122 actionsTree.setCellRenderer(new DefaultTreeCellRenderer() {
123 @Override
124 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
125 boolean leaf, int row, boolean hasFocus) {
126 DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
127 JLabel comp = (JLabel) super.getTreeCellRendererComponent(
128 tree, value, sel, expanded, leaf, row, hasFocus);
129 if (node.getUserObject() == null) {
130 comp.setText(tr("Separator"));
131 comp.setIcon(ImageProvider.get("preferences/separator"));
132 }
133 else if (node.getUserObject() instanceof Action) {
134 Action action = (Action) node.getUserObject();
135 comp.setText((String) action.getValue(Action.NAME));
136 comp.setIcon((Icon) action.getValue(Action.SMALL_ICON));
137 }
138 return comp;
139 }
140 });
141
142 ListCellRenderer renderer = new DefaultListCellRenderer(){
143 @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
144 String s;
145 Icon i;
146 if (value != null) {
147 Action action = getAction((String)value);
148 s = (String) action.getValue(Action.NAME);
149 i = (Icon) action.getValue(Action.SMALL_ICON);
150 } else {
151 i = ImageProvider.get("preferences/separator");
152 s = tr("Separator");
153 }
154 JLabel l = (JLabel)super.getListCellRendererComponent(list, s, index, isSelected, cellHasFocus);
155 l.setIcon(i);
156 return l;
157 }
158 };
159 selectedList.setCellRenderer(renderer);
160 selectedList.addListSelectionListener(new ListSelectionListener(){
161 public void valueChanged(ListSelectionEvent e) {
162 boolean sel = selectedList.getSelectedIndex() != -1;
163 if (sel)
164 actionsTree.clearSelection();
165 upButton.setEnabled(sel);
166 downButton.setEnabled(sel);
167 }
168 });
169
170 selectedList.setDragEnabled(true);
171 selectedList.setTransferHandler(new TransferHandler() {
172 @Override
173 protected Transferable createTransferable(JComponent c) {
174 return new ActionTransferable(((JList)c).getSelectedValues());
175 }
176
177 @Override
178 public int getSourceActions(JComponent c) {
179 return TransferHandler.MOVE;
180 }
181
182 @Override
183 public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
184 for (DataFlavor f : transferFlavors) {
185 if (ACTION_FLAVOR.equals(f)) {
186 return true;
187 }
188 }
189 return false;
190 }
191
192 @Override
193 public void exportAsDrag(JComponent comp, InputEvent e, int action) {
194 super.exportAsDrag(comp, e, action);
195 movingComponent = "list";
196 }
197
198 @Override
199 public boolean importData(JComponent comp, Transferable t) {
200 try {
201 int dropIndex = selectedList.locationToIndex(selectedList.getMousePosition(true));
202 Object[] draggedData = (Object[]) t.getTransferData(ACTION_FLAVOR);
203
204 Object leadItem = dropIndex >= 0 ? selected.elementAt(dropIndex) : null;
205 int dataLength = draggedData.length;
206
207 if (leadItem != null)
208 for (int i = 0; i < dataLength; i++)
209 if (leadItem.equals(draggedData[i]))
210 return false;
211
212 int dragLeadIndex = -1;
213 boolean localDrop = "list".equals(movingComponent);
214
215 if (localDrop) {
216 dragLeadIndex = selected.indexOf(draggedData[0]);
217 for (int i = 0; i < dataLength; i++)
218 selected.removeElement(draggedData[i]);
219 }
220 int[] indices = new int[dataLength];
221
222 if (localDrop) {
223 int adjustedLeadIndex = selected.indexOf(leadItem);
224 int insertionAdjustment = dragLeadIndex <= adjustedLeadIndex ? 1 : 0;
225 for (int i = 0; i < dataLength; i++) {
226 selected.insertElementAt(draggedData[i], adjustedLeadIndex + insertionAdjustment + i);
227 indices[i] = adjustedLeadIndex + insertionAdjustment + i;
228 }
229 } else {
230 for (int i = 0; i < dataLength; i++) {
231 selected.add(dropIndex, draggedData[i]);
232 indices[i] = dropIndex + i;
233 }
234 }
235 selectedList.clearSelection();
236 selectedList.setSelectedIndices(indices);
237 movingComponent = "";
238 return true;
239 } catch (Exception e) {
240 e.printStackTrace();
241 }
242 return false;
243 }
244
245 @Override
246 protected void exportDone(JComponent source, Transferable data, int action) {
247 if (movingComponent.equals("list")) {
248 try {
249 Object[] draggedData = (Object[]) data.getTransferData(ACTION_FLAVOR);
250 boolean localDrop = selected.contains(draggedData[0]);
251 if (localDrop) {
252 int[] indices = selectedList.getSelectedIndices();
253 Arrays.sort(indices);
254 for (int i = indices.length - 1; i >= 0; i--) {
255 selected.remove(indices[i]);
256 }
257 }
258 } catch (Exception e) {
259 e.printStackTrace();
260 }
261 movingComponent = "";
262 }
263 }
264 });
265
266 actionsTree.setTransferHandler(new TransferHandler() {
267 private static final long serialVersionUID = 1L;
268
269 @Override
270 public int getSourceActions( JComponent c ){
271 return TransferHandler.MOVE;
272 }
273
274 @Override
275 protected void exportDone(JComponent source, Transferable data, int action) {
276 }
277
278 @Override
279 protected Transferable createTransferable(JComponent c) {
280 TreePath[] paths = actionsTree.getSelectionPaths();
281 List<String> dragActions = new LinkedList<String>();
282 for (TreePath path : paths) {
283 DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
284 Object obj = node.getUserObject();
285 if (obj == null) {
286 dragActions.add(null);
287 }
288 else if (obj instanceof Action) {
289 dragActions.add((String) ((Action) obj).getValue("toolbar"));
290 }
291 }
292 return new ActionTransferable(dragActions.toArray());
293 }
294 });
295 actionsTree.setDragEnabled(true);
296 }
297
298 public void addGui(PreferenceDialog gui) {
299 final JPanel left = new JPanel(new GridBagLayout());
300 left.add(new JLabel(tr("Toolbar")), GBC.eol());
301 left.add(new JScrollPane(selectedList), GBC.std().fill(GBC.BOTH));
302
303 final JPanel right = new JPanel(new GridBagLayout());
304 right.add(new JLabel(tr("Available")), GBC.eol());
305 right.add(new JScrollPane(actionsTree), GBC.eol().fill(GBC.BOTH));
306
307 final JPanel buttons = new JPanel(new GridLayout(6,1));
308 buttons.add(upButton = createButton("up"));
309 buttons.add(createButton("<"));
310 buttons.add(createButton(">"));
311 buttons.add(downButton = createButton("down"));
312 upButton.setEnabled(false);
313 downButton.setEnabled(false);
314
315 final JPanel p = new JPanel();
316 p.setLayout(new LayoutManager(){
317 public void addLayoutComponent(String name, Component comp) {}
318 public void removeLayoutComponent(Component comp) {}
319 public Dimension minimumLayoutSize(Container parent) {
320 Dimension l = left.getMinimumSize();
321 Dimension r = right.getMinimumSize();
322 Dimension b = buttons.getMinimumSize();
323 return new Dimension(l.width+b.width+10+r.width,l.height+b.height+10+r.height);
324 }
325 public Dimension preferredLayoutSize(Container parent) {
326 Dimension l = new Dimension(200, 200); //left.getPreferredSize();
327 Dimension r = new Dimension(200, 200); //right.getPreferredSize();
328 return new Dimension(l.width+r.width+10+buttons.getPreferredSize().width,Math.max(l.height, r.height));
329 }
330 public void layoutContainer(Container parent) {
331 Dimension d = p.getSize();
332 Dimension b = buttons.getPreferredSize();
333 int width = (d.width-10-b.width)/2;
334 left.setBounds(new Rectangle(0,0,width,d.height));
335 right.setBounds(new Rectangle(width+10+b.width,0,width,d.height));
336 buttons.setBounds(new Rectangle(width+5, d.height/2-b.height/2, b.width, b.height));
337 }
338 });
339 p.add(left);
340 p.add(buttons);
341 p.add(right);
342
343 JPanel panel = gui.createPreferenceTab("toolbar", tr("Toolbar customization"),
344 tr("Customize the elements on the toolbar."), false);
345 panel.add(p, GBC.eol().fill(GBC.BOTH));
346
347 selected.removeAllElements();
348 for (String s : getToolString()) {
349 if (s.equals("|"))
350 selected.addElement(null);
351 else if (getAction(s) != null)
352 selected.addElement(s);
353 }
354 }
355
356 private void loadAction(DefaultMutableTreeNode node, MenuElement menu) {
357 Object userObject = null;
358 MenuElement menuElement = menu;
359 if (menu.getSubElements().length > 0 &&
360 menu.getSubElements()[0] instanceof JPopupMenu) {
361 menuElement = menu.getSubElements()[0];
362 }
363 for (MenuElement item : menuElement.getSubElements()) {
364 if (item instanceof JMenuItem) {
365 JMenuItem menuItem = ((JMenuItem)item);
366 if (menuItem.getAction() != null) {
367 Action action = menuItem.getAction();
368 userObject = action;
369 actions.put((String) action.getValue("toolbar"), action);
370 } else {
371 userObject = menuItem.getText();
372 }
373 }
374 DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(userObject);
375 node.add(newNode);
376 loadAction(newNode, item);
377 }
378 }
379
380 public Action getAction(String s)
381 {
382 Action e = actions.get(s);
383 if(e == null)
384 e = regactions.get(s);
385 return e;
386 }
387
388 private void loadActions() {
389 rootActionsNode.removeAllChildren();
390 loadAction(rootActionsNode, Main.main.menu);
391 for(Map.Entry<String, Action> a : regactions.entrySet())
392 {
393 if(actions.get(a.getKey()) == null)
394 rootActionsNode.add(new DefaultMutableTreeNode(a.getValue()));
395 }
396 rootActionsNode.add(new DefaultMutableTreeNode(null));
397 actionsTree.updateUI();
398 actionsTree.setRootVisible(false);
399 actionsTree.expandPath(new TreePath(rootActionsNode));
400 }
401
402 private static final String[] deftoolbar = {"open", "save", "exportgpx", "|",
403 "download", "upload", "|", "undo", "redo", "|", "preference"};
404
405 private Collection<String> getToolString() {
406 return Main.pref.getCollection("toolbar", Arrays.asList(deftoolbar));
407 }
408
409 private JButton createButton(String name) {
410 JButton b = new JButton();
411 if (name.equals("up"))
412 b.setIcon(ImageProvider.get("dialogs", "up"));
413 else if (name.equals("down"))
414 b.setIcon(ImageProvider.get("dialogs", "down"));
415 else
416 b.setText(name);
417 b.addActionListener(moveAction);
418 b.setActionCommand(name);
419 return b;
420 }
421
422 public boolean ok() {
423 Collection<String> t = new LinkedList<String>();
424 for (int i = 0; i < selected.size(); ++i) {
425 if (selected.get(i) == null)
426 t.add("|");
427 else
428 t.add((String)((getAction((String)selected.get(i))).getValue("toolbar")));
429 }
430 Main.pref.putCollection("toolbar", t);
431 refreshToolbarControl();
432 return false;
433 }
434
435 /**
436 * @return The parameter (for better chaining)
437 */
438 public Action register(Action action) {
439 regactions.put((String) action.getValue("toolbar"), action);
440 return action;
441 }
442
443 /**
444 * Parse the toolbar preference setting and construct the toolbar GUI control.
445 *
446 * Call this, if anything has changed in the toolbar settings and you want to refresh
447 * the toolbar content (e.g. after registering actions in a plugin)
448 */
449 public void refreshToolbarControl() {
450 loadActions();
451 control.removeAll();
452 for (String s : getToolString()) {
453 if (s.equals("|"))
454 control.addSeparator();
455 else
456 control.add(getAction(s));
457 }
458 control.setVisible(control.getComponentCount() != 0);
459 }
460
461 private static DataFlavor ACTION_FLAVOR = new DataFlavor(
462 AbstractAction.class, "ActionItem");
463
464 private class ActionTransferable implements Transferable {
465
466 private DataFlavor[] flavors = new DataFlavor[] { ACTION_FLAVOR };
467
468 private Object[] actions;
469
470 public ActionTransferable(Action action) {
471 this.actions = new Action[] { action };
472 }
473
474 public ActionTransferable(Object[] actions) {
475 this.actions = actions;
476 }
477
478 public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
479 return actions;
480 }
481
482 public DataFlavor[] getTransferDataFlavors() {
483 return flavors;
484 }
485
486 public boolean isDataFlavorSupported(DataFlavor flavor) {
487 return flavors[0] == flavor;
488 }
489 }
490}
Note: See TracBrowser for help on using the repository browser.