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