source: josm/trunk/src/org/openstreetmap/josm/gui/preferences/StyleSourceEditor.java@ 3646

Last change on this file since 3646 was 3646, checked in by bastiK, 14 years ago

little gui rework (move some buttons to the side so the structure is easier to grasp)

  • Property svn:eol-style set to native
File size: 35.7 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.gui.preferences;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.Component;
8import java.awt.GridBagConstraints;
9import java.awt.GridBagLayout;
10import java.awt.event.ActionEvent;
11import java.awt.event.FocusAdapter;
12import java.awt.event.FocusEvent;
13import java.awt.event.KeyEvent;
14import java.awt.event.MouseEvent;
15import java.io.BufferedReader;
16import java.io.File;
17import java.io.IOException;
18import java.io.InputStreamReader;
19import java.io.UnsupportedEncodingException;
20import java.net.MalformedURLException;
21import java.net.URL;
22import java.util.ArrayList;
23import java.util.Collection;
24import java.util.Collections;
25import java.util.Comparator;
26import java.util.EventObject;
27import java.util.Iterator;
28import java.util.LinkedList;
29import java.util.List;
30import java.util.concurrent.CopyOnWriteArrayList;
31import java.util.regex.Matcher;
32import java.util.regex.Pattern;
33
34import javax.swing.AbstractAction;
35import javax.swing.BorderFactory;
36import javax.swing.DefaultListModel;
37import javax.swing.DefaultListSelectionModel;
38import javax.swing.JButton;
39import javax.swing.JComponent;
40import javax.swing.JFileChooser;
41import javax.swing.JLabel;
42import javax.swing.JList;
43import javax.swing.JOptionPane;
44import javax.swing.JPanel;
45import javax.swing.JScrollPane;
46import javax.swing.JSeparator;
47import javax.swing.JTable;
48import javax.swing.JTextField;
49import javax.swing.JToolBar;
50import javax.swing.KeyStroke;
51import javax.swing.ListCellRenderer;
52import javax.swing.ListSelectionModel;
53import javax.swing.event.CellEditorListener;
54import javax.swing.event.ChangeEvent;
55import javax.swing.event.ListSelectionEvent;
56import javax.swing.event.ListSelectionListener;
57import javax.swing.table.AbstractTableModel;
58import javax.swing.table.TableCellEditor;
59
60import org.openstreetmap.josm.Main;
61import org.openstreetmap.josm.gui.HelpAwareOptionPane;
62import org.openstreetmap.josm.gui.PleaseWaitRunnable;
63import org.openstreetmap.josm.io.MirroredInputStream;
64import org.openstreetmap.josm.io.OsmTransferException;
65import org.openstreetmap.josm.tools.GBC;
66import org.openstreetmap.josm.tools.ImageProvider;
67import org.openstreetmap.josm.tools.LanguageInfo;
68import org.xml.sax.SAXException;
69
70public class StyleSourceEditor extends JPanel {
71 private JTable tblActiveStyles;
72 private ActiveStylesModel activeStylesModel;
73 private JList lstAvailableStyles;
74 private AvailableStylesListModel availableStylesModel;
75 private JTable tblIconPaths = null;
76 private IconPathTableModel iconPathsModel;
77 private String pref;
78 private String iconpref;
79 private boolean stylesInitiallyLoaded;
80 private String availableStylesUrl;
81
82 /**
83 *
84 * @param stylesPreferencesKey the preferences key with the list of active style sources (filenames and URLs)
85 * @param iconsPreferenceKey the preference key with the list of icon sources (can be null)
86 * @param availableStylesUrl the URL to the list of available style sources
87 */
88 public StyleSourceEditor(String stylesPreferencesKey, String iconsPreferenceKey, final String availableStylesUrl) {
89
90 DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
91 tblActiveStyles = new JTable(activeStylesModel = new ActiveStylesModel(selectionModel));
92 tblActiveStyles.putClientProperty("terminateEditOnFocusLost", true);
93 tblActiveStyles.setSelectionModel(selectionModel);
94 tblActiveStyles.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
95 tblActiveStyles.setTableHeader(null);
96 tblActiveStyles.getColumnModel().getColumn(0).setCellEditor(new FileOrUrlCellEditor(true));
97 tblActiveStyles.setRowHeight(20);
98 activeStylesModel.setActiveStyles(Main.pref.getCollection(stylesPreferencesKey, null));
99
100 selectionModel = new DefaultListSelectionModel();
101 lstAvailableStyles = new JList(availableStylesModel =new AvailableStylesListModel(selectionModel));
102 lstAvailableStyles.setSelectionModel(selectionModel);
103 lstAvailableStyles.setCellRenderer(new StyleSourceCellRenderer());
104 this.availableStylesUrl = availableStylesUrl;
105
106 this.pref = stylesPreferencesKey;
107 this.iconpref = iconsPreferenceKey;
108
109 EditActiveStyleAction editActiveStyleAction = new EditActiveStyleAction();
110 tblActiveStyles.getSelectionModel().addListSelectionListener(editActiveStyleAction);
111
112 RemoveActiveStylesAction removeActiveStylesAction = new RemoveActiveStylesAction();
113 tblActiveStyles.getSelectionModel().addListSelectionListener(removeActiveStylesAction);
114 tblActiveStyles.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0), "delete");
115 tblActiveStyles.getActionMap().put("delete", removeActiveStylesAction);
116
117 ActivateStylesAction activateStylesAction = new ActivateStylesAction();
118 lstAvailableStyles.addListSelectionListener(activateStylesAction);
119 JButton activate = new JButton(activateStylesAction);
120
121 setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
122 setLayout(new GridBagLayout());
123 add(new JLabel(tr("Active styles:")), GBC.eol().insets(11, 5, 5, 0));
124 JScrollPane sp = new JScrollPane(tblActiveStyles);
125 add(sp, GBC.std().insets(10, 0, 3, 0).fill(GBC.BOTH));
126 sp.setColumnHeaderView(null);
127
128 JToolBar sideButtonTB = new JToolBar(JToolBar.VERTICAL);
129 sideButtonTB.setFloatable(false);
130 sideButtonTB.setBorderPainted(false);
131 sideButtonTB.setOpaque(false);
132 sideButtonTB.add(new NewActiveStyleAction());
133 sideButtonTB.add(editActiveStyleAction);
134 sideButtonTB.add(removeActiveStylesAction);
135 add(sideButtonTB, GBC.eol().insets(0, 0, 10, 0).fill(GBC.VERTICAL));
136
137 JToolBar bottomButtonTB = new JToolBar();
138 bottomButtonTB.setFloatable(false);
139 bottomButtonTB.setBorderPainted(false);
140 bottomButtonTB.setOpaque(false);
141 bottomButtonTB.add(activate);
142 add(bottomButtonTB, GBC.eol().insets(12, 4, 5, 4).fill(GBC.HORIZONTAL));
143
144 add(new JLabel(tr("Available styles (from {0}):", availableStylesUrl)), GBC.eol().insets(11, 0, 5, 0));
145 add(new JScrollPane(lstAvailableStyles), GBC.std().insets(10, 0, 3, 0).fill(GBC.BOTH));
146
147 sideButtonTB = new JToolBar(JToolBar.VERTICAL);
148 sideButtonTB.setFloatable(false);
149 sideButtonTB.setBorderPainted(false);
150 sideButtonTB.setOpaque(false);
151 sideButtonTB.add(new ReloadStylesAction(availableStylesUrl));
152 add(sideButtonTB, GBC.eol().insets(0, 0, 10, 0).fill(GBC.VERTICAL));
153
154 if (iconsPreferenceKey != null) {
155 selectionModel = new DefaultListSelectionModel();
156 tblIconPaths = new JTable(iconPathsModel = new IconPathTableModel(selectionModel));
157 tblIconPaths.setSelectionModel(selectionModel);
158 tblIconPaths.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
159 tblIconPaths.setTableHeader(null);
160 tblIconPaths.getColumnModel().getColumn(0).setCellEditor(new FileOrUrlCellEditor(false));
161 tblIconPaths.setRowHeight(20);
162 iconPathsModel.setIconPaths(Main.pref.getCollection(iconsPreferenceKey, null));
163
164 EditIconPathAction editIconPathAction = new EditIconPathAction();
165 tblIconPaths.getSelectionModel().addListSelectionListener(editIconPathAction);
166
167 RemoveIconPathAction removeIconPathAction = new RemoveIconPathAction();
168 tblIconPaths.getSelectionModel().addListSelectionListener(removeIconPathAction);
169 tblIconPaths.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0), "delete");
170 tblIconPaths.getActionMap().put("delete", removeIconPathAction);
171
172 add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(5, 10, 5, 10));
173 add(new JLabel(tr("Icon paths:")), GBC.eol().insets(11, 0, 5, 0));
174 add(sp = new JScrollPane(tblIconPaths), GBC.std().insets(10, 0, 3, 0).fill(GBC.BOTH));
175 sp.setColumnHeaderView(null);
176 sideButtonTB = new JToolBar(JToolBar.VERTICAL);
177 sideButtonTB.setFloatable(false);
178 sideButtonTB.setBorderPainted(false);
179 sideButtonTB.setOpaque(false);
180 add(sideButtonTB, GBC.eol().insets(0, 0, 10, 0).fill(GBC.VERTICAL));
181 sideButtonTB.add(new NewIconPathAction());
182 sideButtonTB.add(editIconPathAction);
183 sideButtonTB.add(removeIconPathAction);
184 }
185 }
186
187 public boolean hasActiveStylesChanged() {
188 return !activeStylesModel.getStyles().equals(Main.pref.getCollection(pref, Collections.<String>emptyList()));
189 }
190
191 public Collection<String> getActiveStyles() {
192 return activeStylesModel.getStyles();
193 }
194
195 public void removeSource(String source) {
196 activeStylesModel.remove(source);
197 }
198
199 public boolean finish() {
200 boolean changed = false;
201 List<String> activeStyles = activeStylesModel.getStyles();
202
203 if (activeStyles.size() > 0) {
204 if (Main.pref.putCollection(pref, activeStyles)) {
205 changed = true;
206 }
207 } else if (Main.pref.putCollection(pref, null)) {
208 changed = true;
209 }
210
211 if (tblIconPaths != null) {
212 List<String> iconPaths = iconPathsModel.getIconPaths();
213
214 if (!iconPaths.isEmpty()) {
215 if (Main.pref.putCollection(iconpref, iconPaths)) {
216 changed = true;
217 }
218 } else if (Main.pref.putCollection(iconpref, null)) {
219 changed = true;
220 }
221 }
222 return changed;
223 }
224
225 protected void reloadAvailableStyles(String url) {
226 Main.worker.submit(new StyleSourceLoader(url));
227 }
228
229 public void initiallyLoadAvailableStyles() {
230 if (!stylesInitiallyLoaded) {
231 reloadAvailableStyles(this.availableStylesUrl);
232 }
233 stylesInitiallyLoaded = true;
234 }
235
236 static class AvailableStylesListModel extends DefaultListModel {
237 private ArrayList<StyleSourceInfo> data;
238 private DefaultListSelectionModel selectionModel;
239
240 public AvailableStylesListModel(DefaultListSelectionModel selectionModel) {
241 data = new ArrayList<StyleSourceInfo>();
242 this.selectionModel = selectionModel;
243 }
244
245 public void setStyleSources(List<StyleSourceInfo> styleSources) {
246 data.clear();
247 if (styleSources != null) {
248 data.addAll(styleSources);
249 }
250 fireContentsChanged(this, 0, data.size());
251 }
252
253 @Override
254 public Object getElementAt(int index) {
255 return data.get(index);
256 }
257
258 @Override
259 public int getSize() {
260 if (data == null) return 0;
261 return data.size();
262 }
263
264 public void deleteSelected() {
265 Iterator<StyleSourceInfo> it = data.iterator();
266 int i=0;
267 while(it.hasNext()) {
268 it.next();
269 if (selectionModel.isSelectedIndex(i)) {
270 it.remove();
271 }
272 i++;
273 }
274 fireContentsChanged(this, 0, data.size());
275 }
276
277 public List<StyleSourceInfo> getSelected() {
278 ArrayList<StyleSourceInfo> ret = new ArrayList<StyleSourceInfo>();
279 for(int i=0; i<data.size();i++) {
280 if (selectionModel.isSelectedIndex(i)) {
281 ret.add(data.get(i));
282 }
283 }
284 return ret;
285 }
286 }
287
288 static class ActiveStylesModel extends AbstractTableModel {
289 private ArrayList<String> data;
290 private DefaultListSelectionModel selectionModel;
291
292 public ActiveStylesModel(DefaultListSelectionModel selectionModel) {
293 this.selectionModel = selectionModel;
294 this.data = new ArrayList<String>();
295 }
296
297 public int getColumnCount() {
298 return 1;
299 }
300
301 public int getRowCount() {
302 return data == null ? 0 : data.size();
303 }
304
305 public Object getValueAt(int rowIndex, int columnIndex) {
306 return data.get(rowIndex);
307 }
308
309 @Override
310 public boolean isCellEditable(int rowIndex, int columnIndex) {
311 return true;
312 }
313
314 @Override
315 public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
316 updateStyle(rowIndex, (String)aValue);
317 }
318
319 public void setActiveStyles(Collection<String> styles) {
320 data.clear();
321 if (styles !=null) {
322 data.addAll(styles);
323 }
324 sort();
325 fireTableDataChanged();
326 }
327
328 public void addStyle(String style) {
329 if (style == null) return;
330 data.add(style);
331 sort();
332 fireTableDataChanged();
333 int idx = data.indexOf(style);
334 if (idx >= 0) {
335 selectionModel.setSelectionInterval(idx, idx);
336 }
337 }
338
339 public void updateStyle(int pos, String style) {
340 if (style == null) return;
341 if (pos < 0 || pos >= getRowCount()) return;
342 data.set(pos, style);
343 sort();
344 fireTableDataChanged();
345 int idx = data.indexOf(style);
346 if (idx >= 0) {
347 selectionModel.setSelectionInterval(idx, idx);
348 }
349 }
350
351 public void removeSelected() {
352 Iterator<String> it = data.iterator();
353 int i=0;
354 while(it.hasNext()) {
355 it.next();
356 if (selectionModel.isSelectedIndex(i)) {
357 it.remove();
358 }
359 i++;
360 }
361 fireTableDataChanged();
362 }
363
364 public void remove(String source) {
365 data.remove(source);
366 fireTableDataChanged();
367 }
368
369 protected void sort() {
370 Collections.sort(
371 data,
372 new Comparator<String>() {
373 public int compare(String o1, String o2) {
374 if (o1.equals("") && o2.equals(""))
375 return 0;
376 if (o1.equals("")) return 1;
377 if (o2.equals("")) return -1;
378 return o1.compareTo(o2);
379 }
380 }
381 );
382 }
383
384 public void addStylesFromSources(List<StyleSourceInfo> sources) {
385 if (sources == null) return;
386 for (StyleSourceInfo info: sources) {
387 data.add(info.url);
388 }
389 sort();
390 fireTableDataChanged();
391 selectionModel.clearSelection();
392 for (StyleSourceInfo info: sources) {
393 int pos = data.indexOf(info.url);
394 if (pos >=0) {
395 selectionModel.addSelectionInterval(pos, pos);
396 }
397 }
398 }
399
400 public List<String> getStyles() {
401 return new ArrayList<String>(data);
402 }
403
404 public String getStyle(int pos) {
405 return data.get(pos);
406 }
407 }
408
409 public static class StyleSourceInfo {
410 String version;
411 String name;
412 String url;
413 String author;
414 String link;
415 String description;
416 String shortdescription;
417
418 public StyleSourceInfo(String name, String url) {
419 this.name = name;
420 this.url = url;
421 version = author = link = description = shortdescription = null;
422 }
423
424 public String getName() {
425 return shortdescription == null ? name : shortdescription;
426 }
427
428 public String getTooltip() {
429 String s = tr("Short Description: {0}", getName()) + "<br>" + tr("URL: {0}", url);
430 if (author != null) {
431 s += "<br>" + tr("Author: {0}", author);
432 }
433 if (link != null) {
434 s += "<br>" + tr("Webpage: {0}", link);
435 }
436 if (description != null) {
437 s += "<br>" + tr("Description: {0}", description);
438 }
439 if (version != null) {
440 s += "<br>" + tr("Version: {0}", version);
441 }
442 return "<html>" + s + "</html>";
443 }
444
445 @Override
446 public String toString() {
447 return getName() + " (" + url + ")";
448 }
449 }
450
451 class NewActiveStyleAction extends AbstractAction {
452 public NewActiveStyleAction() {
453 putValue(NAME, tr("New"));
454 putValue(SHORT_DESCRIPTION, tr("Add a filename or an URL of an active style"));
455 putValue(SMALL_ICON, ImageProvider.get("dialogs", "add"));
456 }
457
458 public void actionPerformed(ActionEvent e) {
459 activeStylesModel.addStyle("");
460 tblActiveStyles.requestFocusInWindow();
461 tblActiveStyles.editCellAt(activeStylesModel.getRowCount()-1, 0);
462 }
463 }
464
465 class RemoveActiveStylesAction extends AbstractAction implements ListSelectionListener {
466
467 public RemoveActiveStylesAction() {
468 putValue(NAME, tr("Remove"));
469 putValue(SHORT_DESCRIPTION, tr("Remove the selected styles from the list of active styles"));
470 putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
471 updateEnabledState();
472 }
473
474 protected void updateEnabledState() {
475 setEnabled(tblActiveStyles.getSelectedRowCount() > 0);
476 }
477
478 public void valueChanged(ListSelectionEvent e) {
479 updateEnabledState();
480 }
481
482 public void actionPerformed(ActionEvent e) {
483 activeStylesModel.removeSelected();
484 }
485 }
486
487 class EditActiveStyleAction extends AbstractAction implements ListSelectionListener {
488 public EditActiveStyleAction() {
489 putValue(NAME, tr("Edit"));
490 putValue(SHORT_DESCRIPTION, tr("Edit the filename or URL for the selected active style"));
491 putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
492 updateEnabledState();
493 }
494
495 protected void updateEnabledState() {
496 setEnabled(tblActiveStyles.getSelectedRowCount() == 1);
497 }
498
499 public void valueChanged(ListSelectionEvent e) {
500 updateEnabledState();
501 }
502
503 public void actionPerformed(ActionEvent e) {
504 int pos = tblActiveStyles.getSelectedRow();
505 tblActiveStyles.editCellAt(pos, 0);
506 }
507 }
508
509 class ActivateStylesAction extends AbstractAction implements ListSelectionListener {
510 public ActivateStylesAction() {
511 putValue(NAME, tr("Activate"));
512 putValue(SHORT_DESCRIPTION, tr("Add the selected available styles to the list of active styles"));
513 putValue(SMALL_ICON, ImageProvider.get("preferences", "activatestyle"));
514 updateEnabledState();
515 }
516
517 protected void updateEnabledState() {
518 setEnabled(lstAvailableStyles.getSelectedIndices().length > 0);
519 }
520
521 public void valueChanged(ListSelectionEvent e) {
522 updateEnabledState();
523 }
524
525 public void actionPerformed(ActionEvent e) {
526 List<StyleSourceInfo> styleSources = availableStylesModel.getSelected();
527 activeStylesModel.addStylesFromSources(styleSources);
528 }
529 }
530
531 class ReloadStylesAction extends AbstractAction {
532 private String url;
533 public ReloadStylesAction(String url) {
534 putValue(NAME, tr("Reload"));
535 putValue(SHORT_DESCRIPTION, tr("Reloads the list of available styles from ''{0}''", url));
536 putValue(SMALL_ICON, ImageProvider.get("dialogs/refresh"));
537 this.url = url;
538 }
539
540 public void actionPerformed(ActionEvent e) {
541 MirroredInputStream.cleanup(url);
542 reloadAvailableStyles(url);
543 }
544 }
545
546 static class IconPathTableModel extends AbstractTableModel {
547 private ArrayList<String> data;
548 private DefaultListSelectionModel selectionModel;
549
550 public IconPathTableModel(DefaultListSelectionModel selectionModel) {
551 this.selectionModel = selectionModel;
552 this.data = new ArrayList<String>();
553 }
554
555 public int getColumnCount() {
556 return 1;
557 }
558
559 public int getRowCount() {
560 return data == null ? 0 : data.size();
561 }
562
563 public Object getValueAt(int rowIndex, int columnIndex) {
564 return data.get(rowIndex);
565 }
566
567 @Override
568 public boolean isCellEditable(int rowIndex, int columnIndex) {
569 return true;
570 }
571
572 @Override
573 public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
574 updatePath(rowIndex, (String)aValue);
575 }
576
577 public void setIconPaths(Collection<String> styles) {
578 data.clear();
579 if (styles !=null) {
580 data.addAll(styles);
581 }
582 sort();
583 fireTableDataChanged();
584 }
585
586 public void addPath(String path) {
587 if (path == null) return;
588 data.add(path);
589 sort();
590 fireTableDataChanged();
591 int idx = data.indexOf(path);
592 if (idx >= 0) {
593 selectionModel.setSelectionInterval(idx, idx);
594 }
595 }
596
597 public void updatePath(int pos, String path) {
598 if (path == null) return;
599 if (pos < 0 || pos >= getRowCount()) return;
600 data.set(pos, path);
601 sort();
602 fireTableDataChanged();
603 int idx = data.indexOf(path);
604 if (idx >= 0) {
605 selectionModel.setSelectionInterval(idx, idx);
606 }
607 }
608
609 public void removeSelected() {
610 Iterator<String> it = data.iterator();
611 int i=0;
612 while(it.hasNext()) {
613 it.next();
614 if (selectionModel.isSelectedIndex(i)) {
615 it.remove();
616 }
617 i++;
618 }
619 fireTableDataChanged();
620 selectionModel.clearSelection();
621 }
622
623 protected void sort() {
624 Collections.sort(
625 data,
626 new Comparator<String>() {
627 public int compare(String o1, String o2) {
628 if (o1.equals("") && o2.equals(""))
629 return 0;
630 if (o1.equals("")) return 1;
631 if (o2.equals("")) return -1;
632 return o1.compareTo(o2);
633 }
634 }
635 );
636 }
637
638 public List<String> getIconPaths() {
639 return new ArrayList<String>(data);
640 }
641 }
642
643 class NewIconPathAction extends AbstractAction {
644 public NewIconPathAction() {
645 putValue(NAME, tr("New"));
646 putValue(SHORT_DESCRIPTION, tr("Add a new icon path"));
647 putValue(SMALL_ICON, ImageProvider.get("dialogs", "add"));
648 }
649
650 public void actionPerformed(ActionEvent e) {
651 iconPathsModel.addPath("");
652 tblIconPaths.editCellAt(iconPathsModel.getRowCount() -1,0);
653 }
654 }
655
656 class RemoveIconPathAction extends AbstractAction implements ListSelectionListener {
657 public RemoveIconPathAction() {
658 putValue(NAME, tr("Remove"));
659 putValue(SHORT_DESCRIPTION, tr("Remove the selected icon paths"));
660 putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
661 updateEnabledState();
662 }
663
664 protected void updateEnabledState() {
665 setEnabled(tblIconPaths.getSelectedRowCount() > 0);
666 }
667
668 public void valueChanged(ListSelectionEvent e) {
669 updateEnabledState();
670 }
671
672 public void actionPerformed(ActionEvent e) {
673 iconPathsModel.removeSelected();
674 }
675 }
676
677 class EditIconPathAction extends AbstractAction implements ListSelectionListener {
678 public EditIconPathAction() {
679 putValue(NAME, tr("Edit"));
680 putValue(SHORT_DESCRIPTION, tr("Edit the selected icon path"));
681 putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
682 updateEnabledState();
683 }
684
685 protected void updateEnabledState() {
686 setEnabled(tblIconPaths.getSelectedRowCount() == 1);
687 }
688
689 public void valueChanged(ListSelectionEvent e) {
690 updateEnabledState();
691 }
692
693 public void actionPerformed(ActionEvent e) {
694 int row = tblIconPaths.getSelectedRow();
695 tblIconPaths.editCellAt(row, 0);
696 }
697 }
698
699 static class StyleSourceCellRenderer extends JLabel implements ListCellRenderer {
700 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
701 boolean cellHasFocus) {
702 String s = value.toString();
703 setText(s);
704 if (isSelected) {
705 setBackground(list.getSelectionBackground());
706 setForeground(list.getSelectionForeground());
707 } else {
708 setBackground(list.getBackground());
709 setForeground(list.getForeground());
710 }
711 setEnabled(list.isEnabled());
712 setFont(list.getFont());
713 setOpaque(true);
714 setToolTipText(((StyleSourceInfo) value).getTooltip());
715 return this;
716 }
717 }
718
719 class StyleSourceLoader extends PleaseWaitRunnable {
720 private String url;
721 private BufferedReader reader;
722 private boolean canceled;
723
724 public StyleSourceLoader(String url) {
725 super(tr("Loading style sources from ''{0}''", url));
726 this.url = url;
727 }
728
729 @Override
730 protected void cancel() {
731 canceled = true;
732 if (reader!= null) {
733 try {
734 reader.close();
735 } catch(IOException e) {
736 // ignore
737 }
738 }
739 }
740
741 @Override
742 protected void finish() {}
743
744 protected void warn(Exception e) {
745 String emsg = e.getMessage() != null ? e.getMessage() : e.toString();
746 emsg = emsg.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
747 String msg = tr("<html>Failed to load the list of style sources from<br>"
748 + "''{0}''.<br>"
749 + "<br>"
750 + "Details (untranslated):<br>{1}</html>",
751 url, emsg
752 );
753
754 HelpAwareOptionPane.showOptionDialog(
755 Main.parent,
756 msg,
757 tr("Error"),
758 JOptionPane.ERROR_MESSAGE,
759 ht("Preferences/Styles#FailedToLoadStyleSources")
760 );
761 }
762
763 @Override
764 protected void realRun() throws SAXException, IOException, OsmTransferException {
765 LinkedList<StyleSourceInfo> styles = new LinkedList<StyleSourceInfo>();
766 String lang = LanguageInfo.getLanguageCodeXML();
767 try {
768 StyleSourceInfo i = new StyleSourceInfo("elemstyles.xml", "resource://data/elemstyles.xml");
769 i.shortdescription = tr("Internal style");
770 i.description = tr("Internal style to be used as base for runtime switchable overlay styles");
771 styles.add(i);
772 MirroredInputStream stream = new MirroredInputStream(url);
773 InputStreamReader r;
774 try {
775 r = new InputStreamReader(stream, "UTF-8");
776 } catch (UnsupportedEncodingException e) {
777 r = new InputStreamReader(stream);
778 }
779 reader = new BufferedReader(r);
780
781 String line;
782 StyleSourceInfo last = null;
783
784 while ((line = reader.readLine()) != null && !canceled) {
785 if (line.trim().equals("")) {
786 continue; // skip empty lines
787 }
788 if (line.startsWith("\t")) {
789 Matcher m = Pattern.compile("^\t([^:]+): *(.+)$").matcher(line);
790 if (! m.matches()) {
791 System.err.println(tr("Warning: illegal format of entry in style list ''{0}''. Got ''{1}''", url, line));
792 continue;
793 }
794 if (last != null) {
795 String key = m.group(1);
796 String value = m.group(2);
797 if ("author".equals(key) && last.author == null) {
798 last.author = value;
799 } else if ("version".equals(key)) {
800 last.version = value;
801 } else if ("link".equals(key) && last.link == null) {
802 last.link = value;
803 } else if ("description".equals(key) && last.description == null) {
804 last.description = value;
805 } else if ("shortdescription".equals(key) && last.shortdescription == null) {
806 last.shortdescription = value;
807 } else if ((lang + "author").equals(key)) {
808 last.author = value;
809 } else if ((lang + "link").equals(key)) {
810 last.link = value;
811 } else if ((lang + "description").equals(key)) {
812 last.description = value;
813 } else if ((lang + "shortdescription").equals(key)) {
814 last.shortdescription = value;
815 }
816 }
817 } else {
818 last = null;
819 Matcher m = Pattern.compile("^(.+);(.+)$").matcher(line);
820 if (m.matches()) {
821 styles.add(last = new StyleSourceInfo(m.group(1), m.group(2)));
822 } else {
823 System.err.println(tr("Warning: illegal format of entry in style list ''{0}''. Got ''{1}''", url, line));
824 }
825 }
826 }
827 } catch (Exception e) {
828 if (canceled)
829 // ignore the exception and return
830 return;
831 OsmTransferException ex = new OsmTransferException(e);
832 ex.setUrl(url);
833 warn(ex);
834 return;
835 }
836 availableStylesModel.setStyleSources(styles);
837 }
838 }
839
840 class FileOrUrlCellEditor extends JPanel implements TableCellEditor {
841 private JTextField tfFileName;
842 private CopyOnWriteArrayList<CellEditorListener> listeners;
843 private String value;
844 private JFileChooser fileChooser;
845 private boolean isFile;
846
847 protected JFileChooser getFileChooser() {
848 if (fileChooser == null) {
849 this.fileChooser = new JFileChooser();
850 if(!isFile) {
851 fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
852 }
853 }
854 return fileChooser;
855 }
856
857 /**
858 * build the GUI
859 */
860 protected void build() {
861 setLayout(new GridBagLayout());
862 GridBagConstraints gc = new GridBagConstraints();
863 gc.gridx = 0;
864 gc.gridy = 0;
865 gc.fill = GridBagConstraints.BOTH;
866 gc.weightx = 1.0;
867 gc.weighty = 1.0;
868 add(tfFileName = new JTextField(), gc);
869
870 gc.gridx = 1;
871 gc.gridy = 0;
872 gc.fill = GridBagConstraints.BOTH;
873 gc.weightx = 0.0;
874 gc.weighty = 1.0;
875 add(new JButton(new LaunchFileChooserAction()));
876
877 tfFileName.addFocusListener(
878 new FocusAdapter() {
879 @Override
880 public void focusGained(FocusEvent e) {
881 tfFileName.selectAll();
882 }
883 }
884 );
885 }
886
887 public FileOrUrlCellEditor(boolean isFile) {
888 this.isFile = isFile;
889 listeners = new CopyOnWriteArrayList<CellEditorListener>();
890 build();
891 }
892
893 public void addCellEditorListener(CellEditorListener l) {
894 if (l != null) {
895 listeners.addIfAbsent(l);
896 }
897 }
898
899 protected void fireEditingCanceled() {
900 for (CellEditorListener l: listeners) {
901 l.editingCanceled(new ChangeEvent(this));
902 }
903 }
904
905 protected void fireEditingStopped() {
906 for (CellEditorListener l: listeners) {
907 l.editingStopped(new ChangeEvent(this));
908 }
909 }
910
911 public void cancelCellEditing() {
912 fireEditingCanceled();
913 }
914
915 public Object getCellEditorValue() {
916 return value;
917 }
918
919 public boolean isCellEditable(EventObject anEvent) {
920 if (anEvent instanceof MouseEvent)
921 return ((MouseEvent)anEvent).getClickCount() >= 2;
922 return true;
923 }
924
925 public void removeCellEditorListener(CellEditorListener l) {
926 listeners.remove(l);
927 }
928
929 public boolean shouldSelectCell(EventObject anEvent) {
930 return true;
931 }
932
933 public boolean stopCellEditing() {
934 value = tfFileName.getText();
935 fireEditingStopped();
936 return true;
937 }
938
939 public void setInitialValue(String initialValue) {
940 this.value = initialValue;
941 if (initialValue == null) {
942 this.tfFileName.setText("");
943 } else {
944 this.tfFileName.setText(initialValue);
945 }
946 }
947
948 public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
949 setInitialValue((String)value);
950 tfFileName.selectAll();
951 return this;
952 }
953
954 class LaunchFileChooserAction extends AbstractAction {
955 public LaunchFileChooserAction() {
956 putValue(NAME, "...");
957 putValue(SHORT_DESCRIPTION, tr("Launch a file chooser to select a file"));
958 }
959
960 protected void prepareFileChooser(String url, JFileChooser fc) {
961 if (url == null || url.trim().length() == 0) return;
962 URL sourceUrl = null;
963 try {
964 sourceUrl = new URL(url);
965 } catch(MalformedURLException e) {
966 File f = new File(url);
967 if (f.isFile()) {
968 f = f.getParentFile();
969 }
970 if (f != null) {
971 fc.setCurrentDirectory(f);
972 }
973 return;
974 }
975 if (sourceUrl.getProtocol().startsWith("file")) {
976 File f = new File(sourceUrl.getPath());
977 if (f.isFile()) {
978 f = f.getParentFile();
979 }
980 if (f != null) {
981 fc.setCurrentDirectory(f);
982 }
983 }
984 }
985
986 public void actionPerformed(ActionEvent e) {
987 JFileChooser fc = getFileChooser();
988 prepareFileChooser(tfFileName.getText(), fc);
989 int ret = fc.showOpenDialog(JOptionPane.getFrameForComponent(StyleSourceEditor.this));
990 if (ret != JFileChooser.APPROVE_OPTION)
991 return;
992 tfFileName.setText(fc.getSelectedFile().toString());
993 }
994 }
995 }
996
997}
Note: See TracBrowser for help on using the repository browser.