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

Revision 5219, 28.1 KB checked in by bastiK, 2 weeks ago (diff)

mapstyles: add automatic reloading of local styles when they have been edited in an extenal editor (based on mtime)

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