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

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

fixed #2849 - patch by jttt - fix memory leak

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