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