source: josm/trunk/src/org/openstreetmap/josm/gui/MapFrame.java@ 3843

Last change on this file since 3843 was 3843, checked in by bastiK, 13 years ago

first version of mapstyle dialog; still hidden because its unfinished (set mappaintdialog.show=true in advanced pref to show it). switching styles on/off and reload from file is supported at the moment.

  • Property svn:eol-style set to native
File size: 17.2 KB
Line 
1// License: GPL. See LICENSE file for details.
2package org.openstreetmap.josm.gui;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.Container;
8import java.awt.Dimension;
9import java.awt.Font;
10import java.awt.Rectangle;
11import java.awt.event.ActionEvent;
12import java.awt.event.KeyEvent;
13import java.awt.event.MouseWheelEvent;
14import java.awt.event.MouseWheelListener;
15import java.util.ArrayList;
16import java.util.HashMap;
17import java.util.List;
18import java.util.Map;
19import java.util.concurrent.CopyOnWriteArrayList;
20
21import javax.swing.AbstractAction;
22import javax.swing.AbstractButton;
23import javax.swing.Action;
24import javax.swing.BoxLayout;
25import javax.swing.ButtonGroup;
26import javax.swing.JButton;
27import javax.swing.JCheckBoxMenuItem;
28import javax.swing.JComponent;
29import javax.swing.JPanel;
30import javax.swing.JPopupMenu;
31import javax.swing.JSplitPane;
32import javax.swing.JToolBar;
33import javax.swing.KeyStroke;
34import javax.swing.border.Border;
35import javax.swing.plaf.basic.BasicSplitPaneDivider;
36import javax.swing.plaf.basic.BasicSplitPaneUI;
37
38import org.openstreetmap.josm.Main;
39import org.openstreetmap.josm.actions.mapmode.DeleteAction;
40import org.openstreetmap.josm.actions.mapmode.DrawAction;
41import org.openstreetmap.josm.actions.mapmode.ExtrudeAction;
42import org.openstreetmap.josm.actions.mapmode.MapMode;
43import org.openstreetmap.josm.actions.mapmode.SelectAction;
44import org.openstreetmap.josm.actions.mapmode.ZoomAction;
45import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
46import org.openstreetmap.josm.gui.dialogs.ChangesetDialog;
47import org.openstreetmap.josm.gui.dialogs.CommandStackDialog;
48import org.openstreetmap.josm.gui.dialogs.ConflictDialog;
49import org.openstreetmap.josm.gui.dialogs.DialogsPanel;
50import org.openstreetmap.josm.gui.dialogs.FilterDialog;
51import org.openstreetmap.josm.gui.dialogs.HistoryDialog;
52import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
53import org.openstreetmap.josm.gui.dialogs.MapPaintDialog;
54import org.openstreetmap.josm.gui.dialogs.RelationListDialog;
55import org.openstreetmap.josm.gui.dialogs.SelectionListDialog;
56import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
57import org.openstreetmap.josm.gui.dialogs.UserListDialog;
58import org.openstreetmap.josm.gui.dialogs.ValidatorDialog;
59import org.openstreetmap.josm.gui.dialogs.properties.PropertiesDialog;
60import org.openstreetmap.josm.gui.layer.Layer;
61import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
62import org.openstreetmap.josm.tools.Destroyable;
63
64/**
65 * One Map frame with one dataset behind. This is the container gui class whose
66 * display can be set to the different views.
67 *
68 * @author imi
69 */
70public class MapFrame extends JPanel implements Destroyable, LayerChangeListener {
71
72 /**
73 * The current mode, this frame operates.
74 */
75 public MapMode mapMode;
76
77 private final List<MapMode> mapModes = new ArrayList<MapMode>();
78 /**
79 * The view control displayed.
80 */
81 public MapView mapView;
82 /**
83 * The toolbar with the action icons. To add new toggle dialog actions, use addToggleDialog
84 * instead of adding directly to this list. To add a new mode use addMapMode.
85 */
86 private JToolBar toolBarActions = new JToolBar(JToolBar.VERTICAL);
87 private JToolBar toolBarToggle = new JToolBar(JToolBar.VERTICAL);
88 /**
89 * The status line below the map
90 */
91 public MapStatus statusLine;
92
93 public ConflictDialog conflictDialog;
94 public FilterDialog filterDialog;
95 public RelationListDialog relationListDialog;
96 public ValidatorDialog validatorDialog;
97 public SelectionListDialog selectionListDialog;
98 /**
99 * The panel list of all toggle dialog icons. To add new toggle dialog actions, use addToggleDialog
100 * instead of adding directly to this list.
101 */
102 private List<ToggleDialog> allDialogs = new ArrayList<ToggleDialog>();
103 private final DialogsPanel dialogsPanel;
104
105 public final ButtonGroup toolGroup = new ButtonGroup();
106
107 public final JButton otherButton = new JButton(new OtherButtonsAction());
108
109 /**
110 * Default width of the toggle dialog area.
111 */
112 public static final int DEF_TOGGLE_DLG_WIDTH = 330;
113
114 private final Map<Layer, MapMode> lastMapMode = new HashMap<Layer, MapMode>();
115
116 public MapFrame(JPanel contentPane) {
117 setSize(400,400);
118 setLayout(new BorderLayout());
119
120 mapView = new MapView(contentPane);
121
122 new FileDrop(mapView);
123
124 // show menu entry
125 Main.main.menu.viewMenu.setVisible(true);
126
127 // toolbar
128 toolBarActions.setFloatable(false);
129 addMapMode(new IconToggleButton(new SelectAction(this)));
130 addMapMode(new IconToggleButton(new DrawAction(this)));
131 addMapMode(new IconToggleButton(new ExtrudeAction(this)));
132 addMapMode(new IconToggleButton(new ZoomAction(this)));
133 addMapMode(new IconToggleButton(new DeleteAction(this)));
134
135 toolGroup.setSelected(((AbstractButton)toolBarActions.getComponent(0)).getModel(), true);
136
137 JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true);
138 dialogsPanel = new DialogsPanel(splitPane);
139 splitPane.setLeftComponent(mapView);
140 splitPane.setRightComponent(dialogsPanel);
141
142 /**
143 * All additional space goes to the mapView
144 */
145 splitPane.setResizeWeight(1.0);
146
147 /**
148 * Some beautifications.
149 */
150 splitPane.setDividerSize(5);
151 splitPane.setBorder(null);
152 splitPane.setUI(new BasicSplitPaneUI() {
153 @Override
154 public BasicSplitPaneDivider createDefaultDivider() {
155 return new BasicSplitPaneDivider(this) {
156 @Override
157 public void setBorder(Border b) {
158 }
159 };
160 }
161 });
162
163 // JSplitPane supports F6 and F8 shortcuts by default, but we need them for Audio actions
164 splitPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F6, 0), new Object());
165 splitPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0), new Object());
166
167 add(splitPane, BorderLayout.CENTER);
168
169 dialogsPanel.setLayout(new BoxLayout(dialogsPanel, BoxLayout.Y_AXIS));
170 dialogsPanel.setPreferredSize(new Dimension(Main.pref.getInteger("toggleDialogs.width",DEF_TOGGLE_DLG_WIDTH), 0));
171
172 dialogsPanel.setMinimumSize(new Dimension(24, 0));
173 mapView.setMinimumSize(new Dimension(10,0));
174
175 toolBarToggle.setFloatable(false);
176 LayerListDialog.createInstance(this);
177 addToggleDialog(LayerListDialog.getInstance());
178 addToggleDialog(new PropertiesDialog(this));
179 addToggleDialog(selectionListDialog = new SelectionListDialog());
180 addToggleDialog(relationListDialog = new RelationListDialog());
181 addToggleDialog(new CommandStackDialog(this));
182 addToggleDialog(new UserListDialog());
183 addToggleDialog(new HistoryDialog());
184 addToggleDialog(conflictDialog = new ConflictDialog());
185 addToggleDialog(validatorDialog = new ValidatorDialog());
186 addToggleDialog(filterDialog = new FilterDialog());
187 addToggleDialog(new ChangesetDialog(this));
188 if (Main.pref.getBoolean("mappaintdialog.show", false)) {
189 addToggleDialog(new MapPaintDialog());
190 }
191
192 // status line below the map
193 statusLine = new MapStatus(this);
194 MapView.addLayerChangeListener(this);
195 }
196
197 public void selectSelectTool(boolean onlyIfModeless) {
198 if(onlyIfModeless && !Main.pref.getBoolean("modeless", false))
199 return;
200
201 selectMapMode((MapMode)getDefaultButtonAction());
202 }
203
204 public void selectDrawTool(boolean onlyIfModeless) {
205 if(onlyIfModeless && !Main.pref.getBoolean("modeless", false))
206 return;
207
208 Action drawAction = ((AbstractButton)toolBarActions.getComponent(1)).getAction();
209 selectMapMode((MapMode)drawAction);
210 }
211
212 /**
213 * Called as some kind of destructor when the last layer has been removed.
214 * Delegates the call to all Destroyables within this component (e.g. MapModes)
215 */
216 public void destroy() {
217 MapView.removeLayerChangeListener(this);
218 dialogsPanel.destroy();
219 for (int i = 0; i < toolBarActions.getComponentCount(); ++i) {
220 if (toolBarActions.getComponent(i) instanceof Destroyable) {
221 ((Destroyable)toolBarActions.getComponent(i)).destroy();
222 }
223 }
224 for (int i = 0; i < toolBarToggle.getComponentCount(); ++i) {
225 if (toolBarToggle.getComponent(i) instanceof Destroyable) {
226 ((Destroyable)toolBarToggle.getComponent(i)).destroy();
227 }
228 }
229
230 // remove menu entries
231 Main.main.menu.viewMenu.setVisible(false);
232
233 // MapFrame gets destroyed when the last layer is removed, but the status line background
234 // thread that collects the information doesn't get destroyed automatically.
235 if(statusLine.thread != null) {
236 try {
237 statusLine.thread.interrupt();
238 } catch (Exception e) {
239 e.printStackTrace();
240 }
241 }
242 mapView.destroy();
243 }
244
245 public Action getDefaultButtonAction() {
246 return ((AbstractButton)toolBarActions.getComponent(0)).getAction();
247 }
248
249 /**
250 * Open all ToggleDialogs that have their preferences property set. Close all others.
251 */
252 public void initializeDialogsPane() {
253 dialogsPanel.initialize(allDialogs);
254 }
255
256 /**
257 * Call this to add new toggle dialogs to the left button-list
258 * @param dlg The toggle dialog. It must not be in the list already.
259 */
260 public IconToggleButton addToggleDialog(final ToggleDialog dlg) {
261 final IconToggleButton button = new IconToggleButton(dlg.getToggleAction());
262 toolBarToggle.add(button);
263 button.addMouseListener(new PopupMenuLauncher(new JPopupMenu() {
264 {
265 add(new AbstractAction() {
266 {
267 putValue(NAME, tr("Hide this button"));
268 putValue(SHORT_DESCRIPTION, tr("Click the arrow at the bottom to show it again."));
269 }
270
271 @Override
272 public void actionPerformed(ActionEvent e) {
273 dlg.hideButton();
274 }
275 });
276 }
277 }));
278 dlg.setButton(button);
279 allDialogs.add(dlg);
280 if (dialogsPanel.initialized) {
281 dialogsPanel.add(dlg);
282 }
283 return button;
284 }
285
286 public void addMapMode(IconToggleButton b) {
287 toolBarActions.add(b);
288 toolGroup.add(b);
289 if (b.getAction() instanceof MapMode) {
290 mapModes.add((MapMode) b.getAction());
291 } else
292 throw new IllegalArgumentException("MapMode action must be subclass of MapMode");
293 }
294
295 /**
296 * Fires an property changed event "visible".
297 */
298 @Override public void setVisible(boolean aFlag) {
299 boolean old = isVisible();
300 super.setVisible(aFlag);
301 if (old != aFlag) {
302 firePropertyChange("visible", old, aFlag);
303 }
304 }
305
306 /**
307 * Change the operating map mode for the view. Will call unregister on the
308 * old MapMode and register on the new one.
309 * @param mapMode The new mode to set.
310 */
311 public void selectMapMode(MapMode newMapMode) {
312 MapMode oldMapMode = this.mapMode;
313 if (newMapMode == oldMapMode)
314 return;
315 if (oldMapMode != null) {
316 oldMapMode.exitMode();
317 }
318 this.mapMode = newMapMode;
319 newMapMode.enterMode();
320 lastMapMode.put(mapView.getActiveLayer(), newMapMode);
321 fireMapModeChanged(oldMapMode, newMapMode);
322 }
323
324 /**
325 * Fill the given panel by adding all necessary components to the different
326 * locations.
327 *
328 * @param panel The container to fill. Must have an BorderLayout.
329 */
330 public void fillPanel(Container panel) {
331 panel.add(this, BorderLayout.CENTER);
332 JToolBar jb = new JToolBar(JToolBar.VERTICAL);
333 jb.setFloatable(false);
334 toolBarActions.setAlignmentX(0.5f);
335 jb.add(toolBarActions);
336
337 jb.addSeparator(new Dimension(0,10));
338 toolBarToggle.setAlignmentX(0.5f);
339 jb.add(toolBarToggle);
340 otherButton.setAlignmentX(0.5f);
341 otherButton.setBorder(null);
342 otherButton.setFont(otherButton.getFont().deriveFont(Font.PLAIN));
343 jb.add(otherButton);
344
345 if(Main.pref.getBoolean("sidetoolbar.visible", true))
346 {
347 if(Main.pref.getBoolean("sidetoolbar.scrollable", true)) {
348 final ScrollViewport svp = new ScrollViewport(jb, ScrollViewport.VERTICAL_DIRECTION);
349 panel.add(svp, BorderLayout.WEST);
350 jb.addMouseWheelListener(new MouseWheelListener() {
351 public void mouseWheelMoved(MouseWheelEvent e) {
352 svp.scroll(0,e.getUnitsToScroll() * 5);
353 }
354 });
355 } else {
356 panel.add(jb, BorderLayout.WEST);
357 }
358 }
359 if (statusLine != null && Main.pref.getBoolean("statusline.visible", true)) {
360 panel.add(statusLine, BorderLayout.SOUTH);
361 }
362 }
363
364 class OtherButtonsAction extends AbstractAction {
365
366 public OtherButtonsAction() {
367 putValue(NAME, ">>");
368 }
369
370 @Override
371 public void actionPerformed(ActionEvent e) {
372 JPopupMenu menu = new JPopupMenu();
373 for (final ToggleDialog t : allDialogs) {
374 menu.add(new JCheckBoxMenuItem(new AbstractAction() {
375 {
376 putValue(NAME, t.getToggleAction().getValue(NAME));
377 putValue(SMALL_ICON, t.getToggleAction().getValue(SMALL_ICON));
378 putValue(SELECTED_KEY, !t.isButtonHidden());
379 putValue(SHORT_DESCRIPTION, tr("Hide or show this toggle button"));
380 }
381 @Override
382 public void actionPerformed(ActionEvent e) {
383 if ((Boolean) getValue(SELECTED_KEY)) {
384 t.showButton();
385 } else {
386 t.hideButton();
387 }
388 }
389 }));
390 }
391 Rectangle bounds = otherButton.getBounds();
392 menu.show(otherButton, bounds.x+bounds.width, 0);
393 }
394 }
395
396 /**
397 * Replies the instance of a toggle dialog of type <code>type</code> managed by this
398 * map frame
399 *
400 * @param <T>
401 * @param type the class of the toggle dialog, i.e. UserListDialog.class
402 * @return the instance of a toggle dialog of type <code>type</code> managed by this
403 * map frame; null, if no such dialog exists
404 *
405 */
406 public <T> T getToggleDialog(Class<T> type) {
407 return dialogsPanel.getToggleDialog(type);
408 }
409
410 /**
411 * Returns the current width of the (possibly resized) toggle dialog area
412 */
413 public int getToggleDlgWidth() {
414 return dialogsPanel.getWidth();
415 }
416
417 /**
418 * Interface to notify listeners of the change of the mapMode.
419 */
420 public interface MapModeChangeListener {
421 void mapModeChange(MapMode oldMapMode, MapMode newMapMode);
422 }
423
424 /**
425 * the mapMode listeners
426 */
427 private static final CopyOnWriteArrayList<MapModeChangeListener> mapModeChangeListeners = new CopyOnWriteArrayList<MapModeChangeListener>();
428 /**
429 * Adds a mapMode change listener
430 *
431 * @param listener the listener. Ignored if null or already registered.
432 */
433 public static void addMapModeChangeListener(MapModeChangeListener listener) {
434 if (listener != null) {
435 mapModeChangeListeners.addIfAbsent(listener);
436 }
437 }
438 /**
439 * Removes a mapMode change listener
440 *
441 * @param listener the listener. Ignored if null or already registered.
442 */
443 public static void removeMapModeChangeListener(MapModeChangeListener listener) {
444 mapModeChangeListeners.remove(listener);
445 }
446
447 protected static void fireMapModeChanged(MapMode oldMapMode, MapMode newMapMode) {
448 for (MapModeChangeListener l : mapModeChangeListeners) {
449 l.mapModeChange(oldMapMode, newMapMode);
450 }
451 }
452
453 @Override
454 public void activeLayerChange(Layer oldLayer, Layer newLayer) {
455 boolean modeChanged = false;
456 if (mapMode == null || !mapMode.layerIsSupported(newLayer)) {
457 MapMode newMapMode = lastMapMode.get(newLayer);
458 modeChanged = newMapMode != mapMode;
459 if (newMapMode != null) {
460 selectMapMode(newMapMode);
461 } // it would be nice to select first supported mode when layer is first selected, but it don't work well with for example editgpx layer
462 }
463 if (!modeChanged && mapMode != null) {
464 // Let mapmodes know about new active layer
465 mapMode.exitMode();
466 mapMode.enterMode();
467 }
468 // invalidate repaint cache
469 Main.map.mapView.preferenceChanged(null);
470 }
471
472 @Override
473 public void layerAdded(Layer newLayer) { }
474
475 @Override
476 public void layerRemoved(Layer oldLayer) {
477 lastMapMode.remove(oldLayer);
478 }
479}
Note: See TracBrowser for help on using the repository browser.