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