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