001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs; 003 004import static org.openstreetmap.josm.tools.I18n.marktr; 005import static org.openstreetmap.josm.tools.I18n.tr; 006 007import java.awt.Component; 008import java.awt.Dimension; 009import java.awt.Font; 010import java.awt.GridBagLayout; 011import java.awt.Insets; 012import java.awt.Point; 013import java.awt.Rectangle; 014import java.awt.event.ActionEvent; 015import java.awt.event.ActionListener; 016import java.awt.event.KeyEvent; 017import java.awt.event.MouseEvent; 018import java.io.BufferedInputStream; 019import java.io.BufferedReader; 020import java.io.File; 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.InputStreamReader; 024import java.nio.charset.StandardCharsets; 025import java.nio.file.Files; 026import java.nio.file.StandardCopyOption; 027import java.util.ArrayList; 028import java.util.Arrays; 029import java.util.Collection; 030import java.util.List; 031 032import javax.swing.AbstractAction; 033import javax.swing.DefaultButtonModel; 034import javax.swing.DefaultListSelectionModel; 035import javax.swing.ImageIcon; 036import javax.swing.JCheckBox; 037import javax.swing.JFileChooser; 038import javax.swing.JLabel; 039import javax.swing.JMenu; 040import javax.swing.JPanel; 041import javax.swing.JPopupMenu; 042import javax.swing.JScrollPane; 043import javax.swing.JTabbedPane; 044import javax.swing.JTable; 045import javax.swing.JViewport; 046import javax.swing.ListSelectionModel; 047import javax.swing.SingleSelectionModel; 048import javax.swing.SwingConstants; 049import javax.swing.SwingUtilities; 050import javax.swing.UIManager; 051import javax.swing.border.EmptyBorder; 052import javax.swing.event.ChangeEvent; 053import javax.swing.event.ChangeListener; 054import javax.swing.event.ListSelectionEvent; 055import javax.swing.event.ListSelectionListener; 056import javax.swing.filechooser.FileFilter; 057import javax.swing.table.AbstractTableModel; 058import javax.swing.table.DefaultTableCellRenderer; 059import javax.swing.table.TableCellRenderer; 060import javax.swing.table.TableModel; 061 062import org.openstreetmap.josm.Main; 063import org.openstreetmap.josm.actions.ExtensionFileFilter; 064import org.openstreetmap.josm.actions.JosmAction; 065import org.openstreetmap.josm.actions.PreferencesAction; 066import org.openstreetmap.josm.gui.ExtendedDialog; 067import org.openstreetmap.josm.gui.PleaseWaitRunnable; 068import org.openstreetmap.josm.gui.SideButton; 069import org.openstreetmap.josm.gui.mappaint.MapPaintStyles; 070import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.MapPaintSylesUpdateListener; 071import org.openstreetmap.josm.gui.mappaint.StyleSetting; 072import org.openstreetmap.josm.gui.mappaint.StyleSource; 073import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource; 074import org.openstreetmap.josm.gui.preferences.SourceEntry; 075import org.openstreetmap.josm.gui.preferences.map.MapPaintPreference; 076import org.openstreetmap.josm.gui.util.FileFilterAllFiles; 077import org.openstreetmap.josm.gui.util.GuiHelper; 078import org.openstreetmap.josm.gui.widgets.AbstractFileChooser; 079import org.openstreetmap.josm.gui.widgets.FileChooserManager; 080import org.openstreetmap.josm.gui.widgets.HtmlPanel; 081import org.openstreetmap.josm.gui.widgets.JosmTextArea; 082import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher; 083import org.openstreetmap.josm.tools.GBC; 084import org.openstreetmap.josm.tools.ImageOverlay; 085import org.openstreetmap.josm.tools.ImageProvider; 086import org.openstreetmap.josm.tools.ImageProvider.ImageSizes; 087import org.openstreetmap.josm.tools.InputMapUtils; 088import org.openstreetmap.josm.tools.Shortcut; 089import org.openstreetmap.josm.tools.Utils; 090 091public class MapPaintDialog extends ToggleDialog { 092 093 protected StylesTable tblStyles; 094 protected StylesModel model; 095 protected DefaultListSelectionModel selectionModel; 096 097 protected OnOffAction onoffAction; 098 protected ReloadAction reloadAction; 099 protected MoveUpDownAction upAction; 100 protected MoveUpDownAction downAction; 101 protected JCheckBox cbWireframe; 102 103 /** 104 * Action that opens the map paint preferences. 105 */ 106 public static final JosmAction PREFERENCE_ACTION = PreferencesAction.forPreferenceSubTab( 107 tr("Map paint preferences"), null, MapPaintPreference.class, /* ICON */ "dialogs/mappaintpreference"); 108 109 /** 110 * Constructs a new {@code MapPaintDialog}. 111 */ 112 public MapPaintDialog() { 113 super(tr("Map Paint Styles"), "mapstyle", tr("configure the map painting style"), 114 Shortcut.registerShortcut("subwindow:mappaint", tr("Toggle: {0}", tr("MapPaint")), 115 KeyEvent.VK_M, Shortcut.ALT_SHIFT), 150, false, MapPaintPreference.class); 116 build(); 117 } 118 119 protected void build() { 120 model = new StylesModel(); 121 122 cbWireframe = new JCheckBox(); 123 JLabel wfLabel = new JLabel(tr("Wireframe View"), ImageProvider.get("dialogs/mappaint", "wireframe_small"), JLabel.HORIZONTAL); 124 wfLabel.setFont(wfLabel.getFont().deriveFont(Font.PLAIN)); 125 wfLabel.setLabelFor(cbWireframe); 126 127 cbWireframe.setModel(new DefaultButtonModel() { 128 @Override 129 public void setSelected(boolean b) { 130 super.setSelected(b); 131 tblStyles.setEnabled(!b); 132 onoffAction.updateEnabledState(); 133 upAction.updateEnabledState(); 134 downAction.updateEnabledState(); 135 } 136 }); 137 cbWireframe.addActionListener(new ActionListener() { 138 @Override 139 public void actionPerformed(ActionEvent e) { 140 Main.main.menu.wireFrameToggleAction.actionPerformed(null); 141 } 142 }); 143 cbWireframe.setBorder(new EmptyBorder(new Insets(1, 1, 1, 1))); 144 145 tblStyles = new StylesTable(model); 146 tblStyles.setSelectionModel(selectionModel = new DefaultListSelectionModel()); 147 tblStyles.addMouseListener(new PopupMenuHandler()); 148 tblStyles.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE); 149 tblStyles.setBackground(UIManager.getColor("Panel.background")); 150 tblStyles.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 151 tblStyles.setTableHeader(null); 152 tblStyles.getColumnModel().getColumn(0).setMaxWidth(1); 153 tblStyles.getColumnModel().getColumn(0).setResizable(false); 154 tblStyles.getColumnModel().getColumn(0).setCellRenderer(new MyCheckBoxRenderer()); 155 tblStyles.getColumnModel().getColumn(1).setCellRenderer(new StyleSourceRenderer()); 156 tblStyles.setShowGrid(false); 157 tblStyles.setIntercellSpacing(new Dimension(0, 0)); 158 159 JPanel p = new JPanel(new GridBagLayout()); 160 p.add(cbWireframe, GBC.std(0, 0)); 161 p.add(wfLabel, GBC.std(1, 0).weight(1, 0)); 162 p.add(tblStyles, GBC.std(0, 1).span(2).fill()); 163 164 reloadAction = new ReloadAction(); 165 onoffAction = new OnOffAction(); 166 upAction = new MoveUpDownAction(false); 167 downAction = new MoveUpDownAction(true); 168 selectionModel.addListSelectionListener(onoffAction); 169 selectionModel.addListSelectionListener(reloadAction); 170 selectionModel.addListSelectionListener(upAction); 171 selectionModel.addListSelectionListener(downAction); 172 173 // Toggle style on Enter and Spacebar 174 InputMapUtils.addEnterAction(tblStyles, onoffAction); 175 InputMapUtils.addSpacebarAction(tblStyles, onoffAction); 176 177 createLayout(p, true, Arrays.asList( 178 new SideButton(onoffAction, false), 179 new SideButton(upAction, false), 180 new SideButton(downAction, false), 181 new SideButton(PREFERENCE_ACTION, false) 182 )); 183 } 184 185 protected static class StylesTable extends JTable { 186 187 public StylesTable(TableModel dm) { 188 super(dm); 189 } 190 191 public void scrollToVisible(int row, int col) { 192 if (!(getParent() instanceof JViewport)) 193 return; 194 JViewport viewport = (JViewport) getParent(); 195 Rectangle rect = getCellRect(row, col, true); 196 Point pt = viewport.getViewPosition(); 197 rect.setLocation(rect.x - pt.x, rect.y - pt.y); 198 viewport.scrollRectToVisible(rect); 199 } 200 } 201 202 @Override 203 public void showNotify() { 204 MapPaintStyles.addMapPaintSylesUpdateListener(model); 205 Main.main.menu.wireFrameToggleAction.addButtonModel(cbWireframe.getModel()); 206 } 207 208 @Override 209 public void hideNotify() { 210 Main.main.menu.wireFrameToggleAction.removeButtonModel(cbWireframe.getModel()); 211 MapPaintStyles.removeMapPaintSylesUpdateListener(model); 212 } 213 214 protected class StylesModel extends AbstractTableModel implements MapPaintSylesUpdateListener { 215 216 private final Class<?>[] columnClasses = {Boolean.class, StyleSource.class}; 217 218 private transient List<StyleSource> data = new ArrayList<>(); 219 220 /** 221 * Constructs a new {@code StylesModel}. 222 */ 223 public StylesModel() { 224 data = new ArrayList<>(MapPaintStyles.getStyles().getStyleSources()); 225 } 226 227 private StyleSource getRow(int i) { 228 return data.get(i); 229 } 230 231 @Override 232 public int getColumnCount() { 233 return 2; 234 } 235 236 @Override 237 public int getRowCount() { 238 return data.size(); 239 } 240 241 @Override 242 public Object getValueAt(int row, int column) { 243 if (column == 0) 244 return getRow(row).active; 245 else 246 return getRow(row); 247 } 248 249 @Override 250 public boolean isCellEditable(int row, int column) { 251 return column == 0; 252 } 253 254 @Override 255 public Class<?> getColumnClass(int column) { 256 return columnClasses[column]; 257 } 258 259 @Override 260 public void setValueAt(Object aValue, int row, int column) { 261 if (row < 0 || row >= getRowCount() || aValue == null) 262 return; 263 if (column == 0) { 264 MapPaintStyles.toggleStyleActive(row); 265 } 266 } 267 268 /** 269 * Make sure the first of the selected entry is visible in the 270 * views of this model. 271 */ 272 public void ensureSelectedIsVisible() { 273 int index = selectionModel.getMinSelectionIndex(); 274 if (index < 0) return; 275 if (index >= getRowCount()) return; 276 tblStyles.scrollToVisible(index, 0); 277 tblStyles.repaint(); 278 } 279 280 @Override 281 public void mapPaintStylesUpdated() { 282 data = new ArrayList<>(MapPaintStyles.getStyles().getStyleSources()); 283 fireTableDataChanged(); 284 tblStyles.repaint(); 285 } 286 287 @Override 288 public void mapPaintStyleEntryUpdated(int idx) { 289 data = new ArrayList<>(MapPaintStyles.getStyles().getStyleSources()); 290 fireTableRowsUpdated(idx, idx); 291 tblStyles.repaint(); 292 } 293 } 294 295 private class MyCheckBoxRenderer extends JCheckBox implements TableCellRenderer { 296 297 /** 298 * Constructs a new {@code MyCheckBoxRenderer}. 299 */ 300 MyCheckBoxRenderer() { 301 setHorizontalAlignment(SwingConstants.CENTER); 302 setVerticalAlignment(SwingConstants.CENTER); 303 } 304 305 @Override 306 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 307 if (value == null) 308 return this; 309 boolean b = (Boolean) value; 310 setSelected(b); 311 setEnabled(!cbWireframe.isSelected()); 312 return this; 313 } 314 } 315 316 private class StyleSourceRenderer extends DefaultTableCellRenderer { 317 @Override 318 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 319 if (value == null) 320 return this; 321 StyleSource s = (StyleSource) value; 322 JLabel label = (JLabel) super.getTableCellRendererComponent(table, 323 s.getDisplayString(), isSelected, hasFocus, row, column); 324 label.setIcon(s.getIcon()); 325 label.setToolTipText(s.getToolTipText()); 326 label.setEnabled(!cbWireframe.isSelected()); 327 return label; 328 } 329 } 330 331 protected class OnOffAction extends AbstractAction implements ListSelectionListener { 332 /** 333 * Constructs a new {@code OnOffAction}. 334 */ 335 public OnOffAction() { 336 putValue(NAME, tr("On/Off")); 337 putValue(SHORT_DESCRIPTION, tr("Turn selected styles on or off")); 338 putValue(SMALL_ICON, ImageProvider.get("apply")); 339 updateEnabledState(); 340 } 341 342 protected void updateEnabledState() { 343 setEnabled(!cbWireframe.isSelected() && tblStyles.getSelectedRowCount() > 0); 344 } 345 346 @Override 347 public void valueChanged(ListSelectionEvent e) { 348 updateEnabledState(); 349 } 350 351 @Override 352 public void actionPerformed(ActionEvent e) { 353 int[] pos = tblStyles.getSelectedRows(); 354 MapPaintStyles.toggleStyleActive(pos); 355 selectionModel.clearSelection(); 356 for (int p: pos) { 357 selectionModel.addSelectionInterval(p, p); 358 } 359 } 360 } 361 362 /** 363 * The action to move down the currently selected entries in the list. 364 */ 365 protected class MoveUpDownAction extends AbstractAction implements ListSelectionListener { 366 367 private final int increment; 368 369 /** 370 * Constructs a new {@code MoveUpDownAction}. 371 * @param isDown {@code true} to move the entry down, {@code false} to move it up 372 */ 373 public MoveUpDownAction(boolean isDown) { 374 increment = isDown ? 1 : -1; 375 putValue(NAME, isDown ? tr("Down") : tr("Up")); 376 putValue(SMALL_ICON, isDown ? ImageProvider.get("dialogs", "down") : ImageProvider.get("dialogs", "up")); 377 putValue(SHORT_DESCRIPTION, isDown ? tr("Move the selected entry one row down.") : tr("Move the selected entry one row up.")); 378 updateEnabledState(); 379 } 380 381 public void updateEnabledState() { 382 int[] sel = tblStyles.getSelectedRows(); 383 setEnabled(!cbWireframe.isSelected() && MapPaintStyles.canMoveStyles(sel, increment)); 384 } 385 386 @Override 387 public void actionPerformed(ActionEvent e) { 388 int[] sel = tblStyles.getSelectedRows(); 389 MapPaintStyles.moveStyles(sel, increment); 390 391 selectionModel.clearSelection(); 392 for (int row: sel) { 393 selectionModel.addSelectionInterval(row + increment, row + increment); 394 } 395 model.ensureSelectedIsVisible(); 396 } 397 398 @Override 399 public void valueChanged(ListSelectionEvent e) { 400 updateEnabledState(); 401 } 402 } 403 404 protected class ReloadAction extends AbstractAction implements ListSelectionListener { 405 /** 406 * Constructs a new {@code ReloadAction}. 407 */ 408 public ReloadAction() { 409 putValue(NAME, tr("Reload from file")); 410 putValue(SHORT_DESCRIPTION, tr("reload selected styles from file")); 411 putValue(SMALL_ICON, ImageProvider.get("dialogs", "refresh")); 412 setEnabled(getEnabledState()); 413 } 414 415 protected boolean getEnabledState() { 416 if (cbWireframe.isSelected()) 417 return false; 418 int[] pos = tblStyles.getSelectedRows(); 419 if (pos.length == 0) 420 return false; 421 for (int i : pos) { 422 if (!model.getRow(i).isLocal()) 423 return false; 424 } 425 return true; 426 } 427 428 @Override 429 public void valueChanged(ListSelectionEvent e) { 430 setEnabled(getEnabledState()); 431 } 432 433 @Override 434 public void actionPerformed(ActionEvent e) { 435 final int[] rows = tblStyles.getSelectedRows(); 436 MapPaintStyles.reloadStyles(rows); 437 Main.worker.submit(new Runnable() { 438 @Override 439 public void run() { 440 SwingUtilities.invokeLater(new Runnable() { 441 @Override 442 public void run() { 443 selectionModel.clearSelection(); 444 for (int r: rows) { 445 selectionModel.addSelectionInterval(r, r); 446 } 447 } 448 }); 449 } 450 }); 451 } 452 } 453 454 protected class SaveAsAction extends AbstractAction { 455 456 /** 457 * Constructs a new {@code SaveAsAction}. 458 */ 459 public SaveAsAction() { 460 putValue(NAME, tr("Save as...")); 461 putValue(SHORT_DESCRIPTION, tr("Save a copy of this Style to file and add it to the list")); 462 putValue(SMALL_ICON, ImageProvider.get("copy")); 463 setEnabled(tblStyles.getSelectedRows().length == 1); 464 } 465 466 @Override 467 public void actionPerformed(ActionEvent e) { 468 int sel = tblStyles.getSelectionModel().getLeadSelectionIndex(); 469 if (sel < 0 || sel >= model.getRowCount()) 470 return; 471 final StyleSource s = model.getRow(sel); 472 473 FileChooserManager fcm = new FileChooserManager(false, "mappaint.clone-style.lastDirectory", System.getProperty("user.home")); 474 String suggestion = fcm.getInitialDirectory() + File.separator + s.getFileNamePart(); 475 476 FileFilter ff; 477 if (s instanceof MapCSSStyleSource) { 478 ff = new ExtensionFileFilter("mapcss,css,zip", "mapcss", tr("Map paint style file (*.mapcss, *.zip)")); 479 } else { 480 ff = new ExtensionFileFilter("xml,zip", "xml", tr("Map paint style file (*.xml, *.zip)")); 481 } 482 fcm.createFileChooser(false, null, Arrays.asList(ff, FileFilterAllFiles.getInstance()), ff, JFileChooser.FILES_ONLY) 483 .getFileChooser().setSelectedFile(new File(suggestion)); 484 AbstractFileChooser fc = fcm.openFileChooser(); 485 if (fc == null) 486 return; 487 Main.worker.submit(new SaveToFileTask(s, fc.getSelectedFile())); 488 } 489 490 private class SaveToFileTask extends PleaseWaitRunnable { 491 private final StyleSource s; 492 private final File file; 493 494 private boolean canceled; 495 private boolean error; 496 497 SaveToFileTask(StyleSource s, File file) { 498 super(tr("Reloading style sources")); 499 this.s = s; 500 this.file = file; 501 } 502 503 @Override 504 protected void cancel() { 505 canceled = true; 506 } 507 508 @Override 509 protected void realRun() { 510 getProgressMonitor().indeterminateSubTask( 511 tr("Save style ''{0}'' as ''{1}''", s.getDisplayString(), file.getPath())); 512 try { 513 InputStream in = s.getSourceInputStream(); 514 try (InputStream bis = new BufferedInputStream(in)) { 515 Files.copy(bis, file.toPath(), StandardCopyOption.REPLACE_EXISTING); 516 } finally { 517 s.closeSourceInputStream(in); 518 } 519 } catch (IOException e) { 520 Main.warn(e); 521 error = true; 522 } 523 } 524 525 @Override 526 protected void finish() { 527 SwingUtilities.invokeLater(new Runnable() { 528 @Override 529 public void run() { 530 if (!error && !canceled) { 531 SourceEntry se = new SourceEntry(s); 532 se.url = file.getPath(); 533 MapPaintStyles.addStyle(se); 534 tblStyles.getSelectionModel().setSelectionInterval(model.getRowCount() - 1, model.getRowCount() - 1); 535 model.ensureSelectedIsVisible(); 536 } 537 } 538 }); 539 } 540 } 541 } 542 543 /** 544 * Displays information about selected paint style in a new dialog. 545 */ 546 protected class InfoAction extends AbstractAction { 547 548 private boolean errorsTabLoaded; 549 private boolean warningsTabLoaded; 550 private boolean sourceTabLoaded; 551 552 /** 553 * Constructs a new {@code InfoAction}. 554 */ 555 public InfoAction() { 556 putValue(NAME, tr("Info")); 557 putValue(SHORT_DESCRIPTION, tr("view meta information, error log and source definition")); 558 putValue(SMALL_ICON, ImageProvider.get("info")); 559 setEnabled(tblStyles.getSelectedRows().length == 1); 560 } 561 562 @Override 563 public void actionPerformed(ActionEvent e) { 564 int sel = tblStyles.getSelectionModel().getLeadSelectionIndex(); 565 if (sel < 0 || sel >= model.getRowCount()) 566 return; 567 final StyleSource s = model.getRow(sel); 568 ExtendedDialog info = new ExtendedDialog(Main.parent, tr("Map Style info"), new String[] {tr("Close")}); 569 info.setPreferredSize(new Dimension(600, 400)); 570 info.setButtonIcons(new String[] {"ok.png"}); 571 572 final JTabbedPane tabs = new JTabbedPane(); 573 574 tabs.add("Info", buildInfoPanel(s)); 575 JLabel lblInfo = new JLabel(tr("Info")); 576 lblInfo.setFont(lblInfo.getFont().deriveFont(Font.PLAIN)); 577 tabs.setTabComponentAt(0, lblInfo); 578 579 final JPanel pErrors = addErrorOrWarningTab(tabs, lblInfo, 580 s.getErrors(), marktr("Errors"), 1, ImageProvider.get("misc", "error")); 581 final JPanel pWarnings = addErrorOrWarningTab(tabs, lblInfo, 582 s.getWarnings(), marktr("Warnings"), 2, ImageProvider.get("warning-small")); 583 584 final JPanel pSource = new JPanel(new GridBagLayout()); 585 tabs.addTab("Source", pSource); 586 JLabel lblSource = new JLabel(tr("Source")); 587 lblSource.setFont(lblSource.getFont().deriveFont(Font.PLAIN)); 588 tabs.setTabComponentAt(3, lblSource); 589 590 tabs.getModel().addChangeListener(new ChangeListener() { 591 @Override 592 public void stateChanged(ChangeEvent e) { 593 if (!errorsTabLoaded && ((SingleSelectionModel) e.getSource()).getSelectedIndex() == 1) { 594 errorsTabLoaded = true; 595 buildErrorsOrWarningPanel(s.getErrors(), pErrors); 596 } 597 if (!warningsTabLoaded && ((SingleSelectionModel) e.getSource()).getSelectedIndex() == 2) { 598 warningsTabLoaded = true; 599 buildErrorsOrWarningPanel(s.getWarnings(), pWarnings); 600 } 601 if (!sourceTabLoaded && ((SingleSelectionModel) e.getSource()).getSelectedIndex() == 3) { 602 sourceTabLoaded = true; 603 buildSourcePanel(s, pSource); 604 } 605 } 606 }); 607 info.setContent(tabs, false); 608 info.showDialog(); 609 } 610 611 private JPanel addErrorOrWarningTab(final JTabbedPane tabs, JLabel lblInfo, 612 Collection<?> items, String title, int pos, ImageIcon icon) { 613 final JPanel pErrors = new JPanel(new GridBagLayout()); 614 tabs.add(title, pErrors); 615 if (items.isEmpty()) { 616 JLabel lblErrors = new JLabel(tr(title)); 617 lblErrors.setFont(lblInfo.getFont().deriveFont(Font.PLAIN)); 618 lblErrors.setEnabled(false); 619 tabs.setTabComponentAt(pos, lblErrors); 620 tabs.setEnabledAt(pos, false); 621 } else { 622 JLabel lblErrors = new JLabel(tr(title), icon, JLabel.HORIZONTAL); 623 tabs.setTabComponentAt(pos, lblErrors); 624 } 625 return pErrors; 626 } 627 628 private JPanel buildInfoPanel(StyleSource s) { 629 JPanel p = new JPanel(new GridBagLayout()); 630 StringBuilder text = new StringBuilder("<table cellpadding=3>"); 631 text.append(tableRow(tr("Title:"), s.getDisplayString())); 632 if (s.url.startsWith("http://") || s.url.startsWith("https://")) { 633 text.append(tableRow(tr("URL:"), s.url)); 634 } else if (s.url.startsWith("resource://")) { 635 text.append(tableRow(tr("Built-in Style, internal path:"), s.url)); 636 } else { 637 text.append(tableRow(tr("Path:"), s.url)); 638 } 639 if (s.icon != null) { 640 text.append(tableRow(tr("Icon:"), s.icon)); 641 } 642 if (s.getBackgroundColorOverride() != null) { 643 text.append(tableRow(tr("Background:"), Utils.toString(s.getBackgroundColorOverride()))); 644 } 645 text.append(tableRow(tr("Style is currently active?"), s.active ? tr("Yes") : tr("No"))) 646 .append("</table>"); 647 p.add(new JScrollPane(new HtmlPanel(text.toString())), GBC.eol().fill(GBC.BOTH)); 648 return p; 649 } 650 651 private String tableRow(String firstColumn, String secondColumn) { 652 return "<tr><td><b>" + firstColumn + "</b></td><td>" + secondColumn + "</td></tr>"; 653 } 654 655 private void buildSourcePanel(StyleSource s, JPanel p) { 656 JosmTextArea txtSource = new JosmTextArea(); 657 txtSource.setFont(GuiHelper.getMonospacedFont(txtSource)); 658 txtSource.setEditable(false); 659 p.add(new JScrollPane(txtSource), GBC.std().fill()); 660 661 try { 662 InputStream is = s.getSourceInputStream(); 663 try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { 664 String line; 665 while ((line = reader.readLine()) != null) { 666 txtSource.append(line + '\n'); 667 } 668 } finally { 669 s.closeSourceInputStream(is); 670 } 671 } catch (IOException ex) { 672 txtSource.append("<ERROR: failed to read file!>"); 673 } 674 } 675 676 private <T> void buildErrorsOrWarningPanel(Collection<T> items, JPanel p) { 677 JosmTextArea txtErrors = new JosmTextArea(); 678 txtErrors.setFont(GuiHelper.getMonospacedFont(txtErrors)); 679 txtErrors.setEditable(false); 680 p.add(new JScrollPane(txtErrors), GBC.std().fill()); 681 for (T t : items) { 682 txtErrors.append(t + "\n"); 683 } 684 } 685 } 686 687 class PopupMenuHandler extends PopupMenuLauncher { 688 @Override 689 public void launch(MouseEvent evt) { 690 if (cbWireframe.isSelected()) 691 return; 692 super.launch(evt); 693 } 694 695 @Override 696 protected void showMenu(MouseEvent evt) { 697 menu = new MapPaintPopup(); 698 super.showMenu(evt); 699 } 700 } 701 702 /** 703 * The popup menu displayed when right-clicking a map paint entry 704 */ 705 public class MapPaintPopup extends JPopupMenu { 706 /** 707 * Constructs a new {@code MapPaintPopup}. 708 */ 709 public MapPaintPopup() { 710 add(reloadAction); 711 add(new SaveAsAction()); 712 713 JMenu setMenu = new JMenu(tr("Style settings")); 714 setMenu.setIcon(new ImageProvider("preference").setMaxSize(ImageSizes.POPUPMENU).addOverlay( 715 new ImageOverlay(new ImageProvider("dialogs/mappaint", "pencil"), 0.5, 0.5, 1.0, 1.0)).get()); 716 setMenu.setToolTipText(tr("Customize the style")); 717 add(setMenu); 718 719 int sel = tblStyles.getSelectionModel().getLeadSelectionIndex(); 720 StyleSource style = null; 721 if (sel >= 0 && sel < model.getRowCount()) { 722 style = model.getRow(sel); 723 } 724 if (style == null || style.settings.isEmpty()) { 725 setMenu.setEnabled(false); 726 } else { 727 for (StyleSetting s : style.settings) { 728 s.addMenuEntry(setMenu); 729 } 730 } 731 732 addSeparator(); 733 add(new InfoAction()); 734 } 735 } 736}