source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/MapPaintDialog.java@ 3863

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

extended mappaint style dialog

  • Property svn:eol-style set to native
File size: 22.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.Component;
8import java.awt.Dimension;
9import java.awt.Font;
10import java.awt.GridBagLayout;
11import java.awt.Point;
12import java.awt.Rectangle;
13import java.awt.event.ActionEvent;
14import java.awt.event.KeyEvent;
15import java.awt.event.MouseEvent;
16import java.io.BufferedInputStream;
17import java.io.BufferedOutputStream;
18import java.io.BufferedReader;
19import java.io.File;
20import java.io.FileOutputStream;
21import java.io.InputStream;
22import java.io.InputStreamReader;
23import java.io.IOException;
24import java.util.ArrayList;
25import java.util.List;
26
27import javax.swing.AbstractAction;
28import javax.swing.DefaultListSelectionModel;
29import javax.swing.JFileChooser;
30import javax.swing.JLabel;
31import javax.swing.JPanel;
32import javax.swing.JPopupMenu;
33import javax.swing.JScrollPane;
34import javax.swing.JTabbedPane;
35import javax.swing.JTable;
36import javax.swing.JTextArea;
37import javax.swing.JViewport;
38import javax.swing.ListSelectionModel;
39import javax.swing.SingleSelectionModel;
40import javax.swing.SwingUtilities;
41import javax.swing.UIManager;
42import javax.swing.event.ChangeEvent;
43import javax.swing.event.ChangeListener;
44import javax.swing.event.ListSelectionEvent;
45import javax.swing.event.ListSelectionListener;
46import javax.swing.table.AbstractTableModel;
47import javax.swing.table.DefaultTableCellRenderer;
48import javax.swing.table.TableModel;
49
50import org.openstreetmap.josm.Main;
51import org.openstreetmap.josm.actions.SaveActionBase;
52import org.openstreetmap.josm.gui.ExtendedDialog;
53import org.openstreetmap.josm.gui.PleaseWaitRunnable;
54import org.openstreetmap.josm.gui.SideButton;
55import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
56import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.MapPaintSylesUpdateListener;
57import org.openstreetmap.josm.gui.mappaint.StyleSource;
58import org.openstreetmap.josm.gui.preferences.PreferenceDialog;
59import org.openstreetmap.josm.gui.preferences.SourceEntry;
60import org.openstreetmap.josm.gui.widgets.HtmlPanel;
61import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
62import org.openstreetmap.josm.tools.GBC;
63import org.openstreetmap.josm.tools.ImageProvider;
64import org.openstreetmap.josm.tools.Shortcut;
65
66public class MapPaintDialog extends ToggleDialog {
67
68 protected StylesTable tblStyles;
69 protected StylesModel model;
70 protected DefaultListSelectionModel selectionModel;
71
72 protected OnOffAction onoffAction;
73 protected ReloadAction reloadAction;
74
75 public MapPaintDialog() {
76 super(tr("Map Paint Styles"), "mapstyle", tr("configure the map painting style"),
77 Shortcut.registerShortcut("subwindow:authors", tr("Toggle: {0}", tr("Authors")), KeyEvent.VK_M, Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 150);
78 build();
79 }
80
81 protected void build() {
82 JPanel pnl = new JPanel();
83 pnl.setLayout(new BorderLayout());
84
85 model = new StylesModel();
86
87 tblStyles = new StylesTable(model);
88 tblStyles.setSelectionModel(selectionModel= new DefaultListSelectionModel());
89 tblStyles.addMouseListener(new PopupMenuHandler());
90 tblStyles.putClientProperty("terminateEditOnFocusLost", true);
91 tblStyles.setBackground(UIManager.getColor("Panel.background"));
92 tblStyles.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
93 tblStyles.setTableHeader(null);
94 tblStyles.getColumnModel().getColumn(0).setMaxWidth(1);
95 tblStyles.getColumnModel().getColumn(0).setResizable(false);
96 tblStyles.getColumnModel().getColumn(1).setCellRenderer(new StyleSourceRenderer());
97 tblStyles.setShowGrid(false);
98 tblStyles.setIntercellSpacing(new Dimension(0, 0));
99
100 pnl.add(new JScrollPane(tblStyles), BorderLayout.CENTER);
101
102 pnl.add(buildButtonRow(), BorderLayout.SOUTH);
103
104 add(pnl, BorderLayout.CENTER);
105 }
106
107 protected static class StylesTable extends JTable {
108
109 public StylesTable(TableModel dm) {
110 super(dm);
111 }
112
113 public void scrollToVisible(int row, int col) {
114 if (!(getParent() instanceof JViewport))
115 return;
116 JViewport viewport = (JViewport) getParent();
117 Rectangle rect = getCellRect(row, col, true);
118 Point pt = viewport.getViewPosition();
119 rect.setLocation(rect.x - pt.x, rect.y - pt.y);
120 viewport.scrollRectToVisible(rect);
121 }
122 }
123
124 protected JPanel buildButtonRow() {
125 JPanel p = getButtonPanel(4);
126 reloadAction = new ReloadAction();
127 onoffAction = new OnOffAction();
128 MoveUpDownAction up = new MoveUpDownAction(false);
129 MoveUpDownAction down = new MoveUpDownAction(true);
130 selectionModel.addListSelectionListener(onoffAction);
131 selectionModel.addListSelectionListener(reloadAction);
132 selectionModel.addListSelectionListener(up);
133 selectionModel.addListSelectionListener(down);
134 p.add(new SideButton(onoffAction));
135 p.add(new SideButton(up));
136 p.add(new SideButton(down));
137 p.add(new SideButton(new LaunchMapPaintPreferencesAction()));
138
139 return p;
140 }
141
142 @Override
143 public void showNotify() {
144 MapPaintStyles.addMapPaintSylesUpdateListener(model);
145 }
146
147 @Override
148 public void hideNotify() {
149 MapPaintStyles.removeMapPaintSylesUpdateListener(model);
150 }
151
152 protected class StylesModel extends AbstractTableModel implements MapPaintSylesUpdateListener {
153
154 List<StyleSource> data = new ArrayList<StyleSource>();
155
156 public StylesModel() {
157 data = new ArrayList<StyleSource>(MapPaintStyles.getStyles().getStyleSources());
158 }
159
160 private StyleSource getRow(int i) {
161 return data.get(i);
162 }
163
164 @Override
165 public int getColumnCount() {
166 return 2;
167 }
168
169 @Override
170 public int getRowCount() {
171 return data.size();
172 }
173
174 @Override
175 public Object getValueAt(int row, int column) {
176 if (column == 0)
177 return getRow(row).active;
178 else
179 return getRow(row);
180 }
181
182 @Override
183 public boolean isCellEditable(int row, int column) {
184 return column == 0;
185 }
186
187 Class<?>[] columnClasses = {Boolean.class, StyleSource.class};
188
189 @Override
190 public Class<?> getColumnClass(int column) {
191 return columnClasses[column];
192 }
193
194 @Override
195 public void setValueAt(Object aValue, int row, int column) {
196 if (row < 0 || row >= getRowCount() || aValue == null)
197 return;
198 if (column == 0) {
199 MapPaintStyles.toggleStyleActive(row);
200 }
201 }
202
203 /**
204 * Make sure the first of the selected entry is visible in the
205 * views of this model.
206 */
207 public void ensureSelectedIsVisible() {
208 int index = selectionModel.getMinSelectionIndex();
209 if (index < 0) return;
210 if (index >= getRowCount()) return;
211 tblStyles.scrollToVisible(index, 0);
212 tblStyles.repaint();
213 }
214
215 /**
216 * MapPaintSylesUpdateListener interface
217 */
218
219 @Override
220 public void mapPaintStylesUpdated() {
221 data = new ArrayList<StyleSource>(MapPaintStyles.getStyles().getStyleSources());
222 fireTableDataChanged();
223 tblStyles.repaint();
224 }
225
226 @Override
227 public void mapPaintStyleEntryUpdated(int idx) {
228 data = new ArrayList<StyleSource>(MapPaintStyles.getStyles().getStyleSources());
229 fireTableRowsUpdated(idx, idx);
230 tblStyles.repaint();
231 }
232 }
233
234 private static class StyleSourceRenderer extends DefaultTableCellRenderer {
235 @Override
236 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
237 StyleSource s = (StyleSource) value;
238 JLabel label = (JLabel)super.getTableCellRendererComponent(table,
239 s.getDisplayString(), isSelected, hasFocus, row, column);
240 label.setIcon(s.getIcon());
241 label.setToolTipText(s.getToolTipText());
242 return label;
243 }
244 }
245
246 protected class OnOffAction extends AbstractAction implements ListSelectionListener {
247 public OnOffAction() {
248 putValue(SHORT_DESCRIPTION, tr("Turn selected styles on or off"));
249 putValue(SMALL_ICON, ImageProvider.get("apply"));
250 updateEnabledState();
251 }
252
253 protected void updateEnabledState() {
254 setEnabled(tblStyles.getSelectedRowCount() > 0);
255 }
256
257 @Override
258 public void valueChanged(ListSelectionEvent e) {
259 updateEnabledState();
260 }
261
262 @Override
263 public void actionPerformed(ActionEvent e) {
264 int[] pos = tblStyles.getSelectedRows();
265 MapPaintStyles.toggleStyleActive(pos);
266 selectionModel.clearSelection();
267 for (int p: pos) {
268 selectionModel.addSelectionInterval(p, p);
269 }
270 }
271 }
272
273 /**
274 * The action to move down the currently selected entries in the list.
275 */
276 class MoveUpDownAction extends AbstractAction implements ListSelectionListener {
277
278 final int increment;
279
280 public MoveUpDownAction(boolean isDown) {
281 increment = isDown ? 1 : -1;
282 putValue(SMALL_ICON, isDown ? ImageProvider.get("dialogs", "down") : ImageProvider.get("dialogs", "up"));
283 putValue(SHORT_DESCRIPTION, isDown ? tr("Move the selected entry one row down.") : tr("Move the selected entry one row up."));
284 updateEnabledState();
285 }
286
287 public void updateEnabledState() {
288 int[] sel = tblStyles.getSelectedRows();
289 setEnabled(MapPaintStyles.canMoveStyles(sel, increment));
290 }
291
292 @Override
293 public void actionPerformed(ActionEvent e) {
294 int[] sel = tblStyles.getSelectedRows();
295 MapPaintStyles.moveStyles(sel, increment);
296
297 selectionModel.clearSelection();
298 for (int row: sel) {
299 selectionModel.addSelectionInterval(row + increment, row + increment);
300 }
301 model.ensureSelectedIsVisible();
302 }
303
304 public void valueChanged(ListSelectionEvent e) {
305 updateEnabledState();
306 }
307 }
308
309 /**
310 * Opens preferences window and selects the mappaint tab.
311 */
312 class LaunchMapPaintPreferencesAction extends AbstractAction {
313 public LaunchMapPaintPreferencesAction() {
314 putValue(SMALL_ICON, ImageProvider.get("dialogs", "mappaintpreference"));
315 }
316
317 @Override
318 public void actionPerformed(ActionEvent e) {
319 final PreferenceDialog p =new PreferenceDialog(Main.parent);
320 p.selectMapPaintPreferenceTab();
321 p.setVisible(true);
322 }
323 }
324
325 protected class ReloadAction extends AbstractAction implements ListSelectionListener {
326 public ReloadAction() {
327 putValue(NAME, tr("Reload from file"));
328 putValue(SHORT_DESCRIPTION, tr("reload selected styles from file"));
329 putValue(SMALL_ICON, ImageProvider.get("dialogs", "refresh"));
330 updateEnabledState();
331 }
332
333 protected void updateEnabledState() {
334 int[] pos = tblStyles.getSelectedRows();
335 boolean e = pos.length > 0;
336 for (int i : pos) {
337 if (!model.getRow(i).isLocal()) {
338 e = false;
339 break;
340 }
341 }
342 setEnabled(e);
343 }
344
345 @Override
346 public void valueChanged(ListSelectionEvent e) {
347 updateEnabledState();
348 }
349
350 @Override
351 public void actionPerformed(ActionEvent e) {
352 final int[] rows = tblStyles.getSelectedRows();
353 MapPaintStyles.reloadStyles(rows);
354 Main.worker.submit(new Runnable() {
355 @Override
356 public void run() {
357 SwingUtilities.invokeLater(new Runnable() {
358 @Override
359 public void run() {
360 selectionModel.clearSelection();
361 for (int r: rows) {
362 selectionModel.addSelectionInterval(r, r);
363 }
364 }
365 });
366
367 }
368 });
369 }
370 }
371
372 protected class SaveAsAction extends AbstractAction {
373
374 public SaveAsAction() {
375 putValue(NAME, tr("Save as..."));
376 putValue(SHORT_DESCRIPTION, tr("Save a copy of this Style to file and add it to the list"));
377 putValue(SMALL_ICON, ImageProvider.get("copy"));
378 setEnabled(tblStyles.getSelectedRows().length == 1);
379 }
380
381 @Override
382 public void actionPerformed(ActionEvent e) {
383 int sel = tblStyles.getSelectionModel().getLeadSelectionIndex();
384 if (sel < 0 || sel >= model.getRowCount())
385 return;
386 final StyleSource s = model.getRow(sel);
387
388 String curDir = Main.pref.get("mappaint.clone-style.lastDirectory", System.getProperty("user.home"));
389
390 String suggestion = curDir + File.separator + s.getFileNamePart();
391 JFileChooser fc = new JFileChooser();
392 fc.setSelectedFile(new File(suggestion));
393
394 int answer = fc.showSaveDialog(Main.parent);
395 if (answer != JFileChooser.APPROVE_OPTION)
396 return;
397
398 if (!fc.getCurrentDirectory().getAbsolutePath().equals(curDir)) {
399 Main.pref.put("mappaint.clone-style.lastDirectory", fc.getCurrentDirectory().getAbsolutePath());
400 }
401 File file = fc.getSelectedFile();
402
403 if (!SaveActionBase.confirmOverride(file))
404 return;
405
406 Main.worker.submit(new SaveToFileTask(s, file));
407 }
408
409 private class SaveToFileTask extends PleaseWaitRunnable {
410 private StyleSource s;
411 private File file;
412
413 private boolean canceled;
414 private boolean error;
415
416 public SaveToFileTask(StyleSource s, File file) {
417 super(tr("Reloading style sources"));
418 this.s = s;
419 this.file = file;
420 }
421
422 @Override
423 protected void cancel() {
424 canceled = true;
425 }
426
427 @Override
428 protected void realRun() {
429 getProgressMonitor().indeterminateSubTask(
430 tr("Save style ''{0}'' as ''{1}''", s.getDisplayString(), file.getPath()));
431 BufferedInputStream bis = null;
432 BufferedOutputStream bos = null;
433 try {
434 bis = new BufferedInputStream(s.getSourceInputStream());
435 bos = new BufferedOutputStream(new FileOutputStream(file));
436 byte[] buffer = new byte[4096];
437 int length;
438 while ((length = bis.read(buffer)) > -1 && !canceled) {
439 bos.write(buffer, 0, length);
440 }
441 } catch (IOException e) {
442 error = true;
443 } finally {
444 if (bis != null) {
445 try {
446 bis.close();
447 } catch (IOException e) {
448 e.printStackTrace();
449 }
450 }
451 if (bos != null) {
452 try {
453 bos.close();
454 } catch (IOException e) {
455 e.printStackTrace();
456 }
457 }
458 }
459 }
460
461 @Override
462 protected void finish() {
463 SwingUtilities.invokeLater(new Runnable() {
464 @Override
465 public void run() {
466 if (!error && !canceled) {
467 SourceEntry se = new SourceEntry(s);
468 se.url = file.getPath();
469 MapPaintStyles.addStyle(se);
470 tblStyles.getSelectionModel().setSelectionInterval(model.getRowCount() - 1 , model.getRowCount() - 1);
471 model.ensureSelectedIsVisible();
472 }
473 }
474 });
475 }
476 }
477 }
478
479 protected class InfoAction extends AbstractAction {
480
481 boolean errorsTabLoaded;
482 boolean sourceTabLoaded;
483
484 public InfoAction() {
485 putValue(NAME, tr("Info"));
486 putValue(SHORT_DESCRIPTION, tr("view meta information, error log and source definition"));
487 putValue(SMALL_ICON, ImageProvider.get("info"));
488 setEnabled(tblStyles.getSelectedRows().length == 1);
489 }
490
491 @Override
492 public void actionPerformed(ActionEvent e) {
493 int sel = tblStyles.getSelectionModel().getLeadSelectionIndex();
494 if (sel < 0 || sel >= model.getRowCount())
495 return;
496 final StyleSource s = model.getRow(sel);
497 ExtendedDialog info = new ExtendedDialog(Main.parent, tr("Map Style info"), new String[] {"Close"});
498 info.setPreferredSize(new Dimension(600, 400));
499 info.setButtonIcons(new String[] {"ok.png"});
500
501 final JTabbedPane tabs = new JTabbedPane();
502
503 tabs.add("Info", buildInfoPanel(s));
504 JLabel lblInfo = new JLabel(tr("Info"));
505 lblInfo.setFont(lblInfo.getFont().deriveFont(Font.PLAIN));
506 tabs.setTabComponentAt(0, lblInfo);
507
508 final JPanel pErrors = new JPanel(new GridBagLayout());
509 tabs.add("Errors", pErrors);
510 JLabel lblErrors;
511 if (s.getErrors().isEmpty()) {
512 lblErrors = new JLabel(tr("Errors"));
513 lblErrors.setFont(lblInfo.getFont().deriveFont(Font.PLAIN));
514 lblErrors.setEnabled(false);
515 tabs.setTabComponentAt(1, lblErrors);
516 tabs.setEnabledAt(1, false);
517 } else {
518 lblErrors = new JLabel(tr("Errors"), ImageProvider.get("misc", "error"), JLabel.HORIZONTAL);
519 tabs.setTabComponentAt(1, lblErrors);
520 }
521
522 final JPanel pSource = new JPanel(new GridBagLayout());
523 tabs.addTab("Source", pSource);
524 JLabel lblSource = new JLabel(tr("Source"));
525 lblSource.setFont(lblSource.getFont().deriveFont(Font.PLAIN));
526 tabs.setTabComponentAt(2, lblSource);
527
528 tabs.getModel().addChangeListener(new ChangeListener() {
529 @Override
530 public void stateChanged(ChangeEvent e) {
531 if (!errorsTabLoaded && ((SingleSelectionModel) e.getSource()).getSelectedIndex() == 1) {
532 errorsTabLoaded = true;
533 buildErrorsPanel(s, pErrors);
534 }
535 if (!sourceTabLoaded && ((SingleSelectionModel) e.getSource()).getSelectedIndex() == 2) {
536 sourceTabLoaded = true;
537 buildSourcePanel(s, pSource);
538 }
539 }
540 });
541 info.setContent(tabs, false);
542 info.showDialog();
543 }
544
545 private JPanel buildInfoPanel(StyleSource s) {
546 JPanel p = new JPanel(new GridBagLayout());
547 StringBuilder text = new StringBuilder("<table cellpadding=3><tr><td>");
548 text.append("<b>" + tr("Name:") + "</b></td><td>" + s.getDisplayString() + "</td></tr>");
549 text.append("<tr><td>");
550 if (s.url.startsWith("http://")) {
551 text.append("<b>" + tr("URL:") + "</b></td><td>" + s.url + "</td></tr>");
552 } else if (s.url.startsWith("resource://")) {
553 text.append("<b>" + tr("Built-in Style, internal path:</b>") + "</b></td><td>" + s.url + "</td></tr>");
554 } else {
555 text.append("<b>" + tr("Path:") + "</b></td><td>" + s.url + "</td></tr>");
556 }
557 text.append("<tr><td><b>" + tr("Style is currently active?") + "</b></td><td>" + (s.active ? tr("Yes") : tr("No")) + "</td></tr>");
558 text.append("</table>");
559 p.add(new JScrollPane(new HtmlPanel(text.toString())), GBC.eol().fill(GBC.BOTH));
560 return p;
561 }
562
563 private void buildSourcePanel(StyleSource s, JPanel p) {
564 JTextArea txtSource = new JTextArea();
565 txtSource.setFont(new Font("Monospaced", txtSource.getFont().getStyle(), txtSource.getFont().getSize()));
566 txtSource.setEditable(false);
567 p.add(new JScrollPane(txtSource), GBC.std().fill());
568
569 InputStream is = null;
570 try {
571 is = s.getSourceInputStream();
572 BufferedReader reader = new BufferedReader(new InputStreamReader(is));
573 String line;
574 while ((line = reader.readLine()) != null) {
575 txtSource.append(line + "\n");
576 }
577 } catch (IOException ex) {
578 txtSource.append("<ERROR: failed to read file!>");
579 } finally {
580 try {
581 is.close();
582 } catch (IOException ex) {
583 }
584 }
585 }
586
587 private void buildErrorsPanel(StyleSource s, JPanel p) {
588 JTextArea txtErrors = new JTextArea();
589 txtErrors.setFont(new Font("Monospaced", txtErrors.getFont().getStyle(), txtErrors.getFont().getSize()));
590 txtErrors.setEditable(false);
591 p.add(new JScrollPane(txtErrors), GBC.std().fill());
592 for (Throwable t : s.getErrors()) {
593 txtErrors.append(t.toString() + "\n");
594 }
595 }
596 }
597
598 class PopupMenuHandler extends PopupMenuLauncher {
599 @Override
600 public void launch(MouseEvent evt) {
601 Point p = evt.getPoint();
602 int index = tblStyles.rowAtPoint(p);
603 if (index < 0) return;
604 if (!tblStyles.getCellRect(index, 1, false).contains(evt.getPoint()))
605 return;
606 if (!tblStyles.isRowSelected(index)) {
607 tblStyles.setRowSelectionInterval(index, index);
608 }
609 MapPaintPopup menu = new MapPaintPopup();
610 menu.show(MapPaintDialog.this, p.x, p.y);
611 }
612 }
613
614 public class MapPaintPopup extends JPopupMenu {
615 public MapPaintPopup() {
616 add(reloadAction);
617 add(new SaveAsAction());
618 addSeparator();
619 add(new InfoAction());
620 }
621 }
622}
Note: See TracBrowser for help on using the repository browser.