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