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