/* * $Id$ * * Copyright (c) 2003-2013 by David Sullivan, Rodney Kinney, * Brent Easton, and Joel Uckelman. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License (LGPL) as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, copies are available * at http://www.opensource.org. */ package VASSAL.build.module.map; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.dnd.DragSource; import java.awt.dnd.DragSourceDragEvent; import java.awt.dnd.DragSourceMotionListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.swing.JComponent; import javax.swing.KeyStroke; import javax.swing.Timer; import VASSAL.build.AbstractConfigurable; import VASSAL.build.AutoConfigurable; import VASSAL.build.Buildable; import VASSAL.build.GameModule; import VASSAL.build.IllegalBuildException; import VASSAL.build.module.Map; import VASSAL.build.module.documentation.HelpFile; import VASSAL.build.module.map.boardPicker.Board; import VASSAL.build.module.map.boardPicker.board.mapgrid.Zone; import VASSAL.build.module.properties.SumProperties; import VASSAL.configure.BooleanConfigurer; import VASSAL.configure.ColorConfigurer; import VASSAL.configure.Configurer; import VASSAL.configure.ConfigurerFactory; import VASSAL.configure.FormattedStringConfigurer; import VASSAL.configure.HotKeyConfigurer; import VASSAL.configure.IntConfigurer; import VASSAL.configure.PropertyExpression; import VASSAL.configure.SingleChildInstance; import VASSAL.configure.StringArrayConfigurer; import VASSAL.configure.StringEnum; import VASSAL.configure.VisibilityCondition; import VASSAL.counters.BasicPiece; import VASSAL.counters.ColoredBorder; import VASSAL.counters.Deck; import VASSAL.counters.DeckVisitorDispatcher; import VASSAL.counters.GamePiece; import VASSAL.counters.Labeler; import VASSAL.counters.PieceFilter; import VASSAL.counters.PieceFinder; import VASSAL.counters.PieceIterator; import VASSAL.counters.Properties; import VASSAL.counters.Stack; import VASSAL.i18n.Resources; import VASSAL.tools.FormattedString; /** * This is a {@link Drawable} class that draws the counters horizontally when * the mouse is held over a stack with the control key down. * * @author David Sullivan * @version 1.0 */ public class CounterDetailViewer extends AbstractConfigurable implements Drawable, DragSourceMotionListener, MouseMotionListener, MouseListener, KeyListener { public static final String LATEST_VERSION = "2"; public static final String USE_KEYBOARD = "ShowCounterDetails"; public static final String PREFERRED_DELAY = "PreferredDelay"; public static final String DELAY = "delay"; public static final String ALWAYS_SHOW_LOC = "alwaysshowloc"; public static final String DRAW_PIECES = "showgraph"; public static final String GRAPH_SINGLE_DEPRECATED = "showgraphsingle"; public static final String MINIMUM_DISPLAYABLE = "minDisplayPieces"; public static final String HOTKEY = "hotkey"; public static final String SHOW_TEXT = "showtext"; public static final String SHOW_TEXT_SINGLE_DEPRECATED = "showtextsingle"; public static final String ZOOM_LEVEL = "zoomlevel"; public static final String DRAW_PIECES_AT_ZOOM = "graphicsZoom"; public static final String BORDER_WIDTH = "borderWidth"; public static final String SHOW_NOSTACK = "showNoStack"; public static final String SHOW_MOVE_SELECTED = "showMoveSelectde"; public static final String SHOW_NON_MOVABLE = "showNonMovable"; public static final String SHOW_DECK = "showDeck"; public static final String UNROTATE_PIECES = "unrotatePieces"; public static final String DISPLAY = "display"; public static final String LAYER_LIST = "layerList"; public static final String SUMMARY_REPORT_FORMAT = "summaryReportFormat"; public static final String COUNTER_REPORT_FORMAT = "counterReportFormat"; public static final String EMPTY_HEX_REPORT_FORMAT = "emptyHexReportForma"; public static final String VERSION = "version"; public static final String FG_COLOR = "fgColor"; public static final String BG_COLOR = "bgColor"; public static final String FONT_SIZE = "fontSize"; public static final String PROPERTY_FILTER = "propertyFilter"; public static final String TOP_LAYER = "from top-most layer only"; public static final String ALL_LAYERS = "from all layers"; public static final String INC_LAYERS = "from listed layers only"; public static final String EXC_LAYERS = "from layers other than those listed"; public static final String FILTER = "by using a property filter"; public static final String SUM = "sum(propertyName)"; protected KeyStroke hotkey = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.CTRL_MASK); protected Map map; protected int delay = 700; protected Timer delayTimer; protected boolean graphicsVisible = false; protected boolean textVisible = false; protected MouseEvent currentMousePosition; protected int minimumDisplayablePieces = 2; protected boolean alwaysShowLoc = false; protected boolean drawPieces = true; protected boolean drawSingleDeprecated = false; protected boolean showText = false; protected boolean showTextSingleDeprecated = false; protected boolean unrotatePieces = false; protected boolean showDeck = false; protected double zoomLevel = 1.0; protected double graphicsZoomLevel = 1.0; protected int borderWidth = 0; protected boolean showNoStack = false; protected boolean showMoveSelected = false; protected boolean showNonMovable = false; protected String displayWhat = TOP_LAYER; protected String[] displayLayers = new String[0]; protected FormattedString summaryReportFormat = new FormattedString("$" + BasicPiece.LOCATION_NAME + "$"); protected FormattedString counterReportFormat = new FormattedString(""); protected FormattedString emptyHexReportFormat = new FormattedString("$" + BasicPiece.LOCATION_NAME + "$"); protected String version = ""; protected Color fgColor = Color.black; protected Color bgColor; protected int fontSize = 9; protected PropertyExpression propertyFilter = new PropertyExpression(); protected Rectangle bounds; protected boolean mouseInView = true; protected List<GamePiece> displayablePieces = null; /** the JComponent which is repainted when the detail viewer changes */ protected JComponent view; public CounterDetailViewer() { // Set up the timer; this isn't the real delay---we always check the // preferences for that. delayTimer = new Timer(delay, new ActionListener() { public void actionPerformed(ActionEvent e) { if (mouseInView) showDetails(); } }); delayTimer.setRepeats(false); } public void addTo(Buildable b) { map = (Map) b; view = map.getView(); validator = new SingleChildInstance(map, getClass()); map.addDrawComponent(this); String keyDesc = hotkey == null ? "" : "(" + HotKeyConfigurer.getString(hotkey) + ")"; GameModule.getGameModule().getPrefs().addOption(Resources.getString("Prefs.general_tab"), new BooleanConfigurer(USE_KEYBOARD, Resources.getString("CounterDetailViewer.use_prompt", keyDesc), Boolean.FALSE)); GameModule.getGameModule().getPrefs().addOption(Resources.getString("Prefs.general_tab"), new IntConfigurer(PREFERRED_DELAY, Resources.getString("CounterDetailViewer.delay_prompt"), delay)); view.addMouseMotionListener(this); view.addMouseListener(this); view.addKeyListener(this); DragSource.getDefaultDragSource().addDragSourceMotionListener(this); setAttributeTranslatable(VERSION, false); setAttributeTranslatable(SUMMARY_REPORT_FORMAT, true); setAttributeTranslatable(COUNTER_REPORT_FORMAT, true); } public void draw(Graphics g, Map map) { if (currentMousePosition != null && view.getVisibleRect().contains(currentMousePosition.getPoint())) { draw(g, currentMousePosition.getPoint(), view); } } public boolean drawAboveCounters() { return true; } public void draw(Graphics g, Point pt, JComponent comp) { if (!graphicsVisible && !textVisible) { return; } bounds = new Rectangle(pt.x, pt.y, 0, 0); if (graphicsVisible) { drawGraphics(g, pt, comp, displayablePieces); } if (textVisible) { drawText(g, pt, comp, displayablePieces); } } @Deprecated // Required for backward compatibility protected void drawGraphics(Graphics g, Point pt, JComponent comp, PieceIterator pi) { ArrayList<GamePiece> a = new ArrayList<GamePiece>(); while (pi.hasMoreElements()) { a.add(pi.nextPiece()); } drawGraphics(g, pt, comp, a); } protected void drawGraphics(Graphics g, Point pt, JComponent comp, List<GamePiece> pieces) { Object owner = null; fixBounds(pieces); if (bounds.width > 0) { Rectangle visibleRect = comp.getVisibleRect(); bounds.x = Math.min(bounds.x, visibleRect.x + visibleRect.width - bounds.width); if (bounds.x < visibleRect.x) bounds.x = visibleRect.x; bounds.y = Math.min(bounds.y, visibleRect.y + visibleRect.height - bounds.height) - (isTextUnderCounters() ? 15 : 0); int minY = visibleRect.y + (textVisible ? g.getFontMetrics().getHeight() + 6 : 0); if (bounds.y < minY) bounds.y = minY; if (bgColor != null) { g.setColor(bgColor); g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); } if (fgColor != null) { g.setColor(fgColor); g.drawRect(bounds.x - 1, bounds.y - 1, bounds.width + 1, bounds.height + 1); g.drawRect(bounds.x - 2, bounds.y - 2, bounds.width + 3, bounds.height + 3); } Shape oldClip = g.getClip(); int borderOffset = borderWidth; double graphicsZoom = graphicsZoomLevel; for (int i = 0; i < pieces.size(); i++) { // Draw the next piece // pt is the location of the left edge of the piece GamePiece piece = pieces.get(i); Rectangle pieceBounds = getBounds(piece); if (unrotatePieces) piece.setProperty(Properties.USE_UNROTATED_SHAPE, Boolean.TRUE); g.setClip(bounds.x - 3, bounds.y - 3, bounds.width + 5, bounds.height + 5); final Stack parent = piece.getParent(); if (parent instanceof Deck) { owner = piece.getProperty(Properties.OBSCURED_BY); final boolean faceDown = ((Deck) parent).isFaceDown(); piece.setProperty(Properties.OBSCURED_BY, faceDown ? Deck.NO_USER : null); } piece.draw(g, bounds.x - (int) (pieceBounds.x * graphicsZoom) + borderOffset, bounds.y - (int) (pieceBounds.y * graphicsZoom) + borderWidth, comp, graphicsZoom); if (parent instanceof Deck) piece.setProperty(Properties.OBSCURED_BY, owner); if (unrotatePieces) piece.setProperty(Properties.USE_UNROTATED_SHAPE, Boolean.FALSE); g.setClip(oldClip); if (isTextUnderCounters()) { String text = counterReportFormat.getLocalizedText(piece); if (text.length() > 0) { int x = bounds.x - (int) (pieceBounds.x * graphicsZoom) + borderOffset; int y = bounds.y + bounds.height + 10; drawLabel(g, new Point(x, y), text, Labeler.CENTER, Labeler.CENTER); } } bounds.translate((int) (pieceBounds.width * graphicsZoom), 0); borderOffset += borderWidth; } } } /** Set the bounds field large enough to accommodate the given set of pieces */ protected void fixBounds(List<GamePiece> pieces) { for (GamePiece piece : pieces) { final Dimension pieceBounds = getBounds(piece).getSize(); bounds.width += (int) Math.round(pieceBounds.width * graphicsZoomLevel) + borderWidth; bounds.height = Math.max(bounds.height, (int) Math.round(pieceBounds.height * graphicsZoomLevel) + borderWidth * 2); } bounds.width += borderWidth; bounds.y -= bounds.height; } protected Rectangle getBounds(GamePiece piece) { if (unrotatePieces) piece.setProperty(Properties.USE_UNROTATED_SHAPE, Boolean.TRUE); Rectangle pieceBounds = piece.getShape().getBounds(); if (unrotatePieces) piece.setProperty(Properties.USE_UNROTATED_SHAPE, Boolean.FALSE); return pieceBounds; } protected boolean isTextUnderCounters() { return textVisible && counterReportFormat.getFormat().length() > 0; } @Deprecated // Required for backward compatibility protected void drawText(Graphics g, Point pt, JComponent comp, PieceIterator pi) { ArrayList<GamePiece> a = new ArrayList<GamePiece>(); while (pi.hasMoreElements()) { a.add(pi.nextPiece()); } drawText(g, pt, comp, a); } protected void drawText(Graphics g, Point pt, JComponent comp, List<GamePiece> pieces) { /* * Label with the location If the counter viewer is being displayed, then * place the location name just above the left hand end of the counters. If * no counter viewer (i.e. single piece or expanded stack), then place the * location name above the centre of the first piece in the stack. */ String report = ""; int x = bounds.x - bounds.width; int y = bounds.y - 5; String offboard = Resources.getString("Map.offboard"); //$NON-NLS-1$ if (displayablePieces.isEmpty()) { Point mapPt = map.mapCoordinates(currentMousePosition.getPoint()); Point snapPt = map.snapTo(mapPt); String locationName = map.localizedLocationName(snapPt); emptyHexReportFormat.setProperty(BasicPiece.LOCATION_NAME, locationName.equals(offboard) ? "" : locationName); emptyHexReportFormat.setProperty(BasicPiece.CURRENT_MAP, map.getLocalizedMapName()); Board b = map.findBoard(snapPt); String boardName = (b == null) ? "" : b.getLocalizedName(); emptyHexReportFormat.setProperty(BasicPiece.CURRENT_BOARD, boardName); Zone z = map.findZone(snapPt); String zone = (z == null) ? "" : z.getLocalizedName(); emptyHexReportFormat.setProperty(BasicPiece.CURRENT_ZONE, zone); report = emptyHexReportFormat.getLocalizedText(); x -= g.getFontMetrics().stringWidth(report) / 2; } else { GamePiece topPiece = displayablePieces.get(0); String locationName = (String) topPiece.getLocalizedProperty(BasicPiece.LOCATION_NAME); emptyHexReportFormat.setProperty(BasicPiece.LOCATION_NAME, locationName.equals(offboard) ? "" : locationName); report = summaryReportFormat.getLocalizedText(new SumProperties(displayablePieces)); x += borderWidth * pieces.size() + 2; } if (report.length() > 0) { drawLabel(g, new Point(x, y), report, Labeler.RIGHT, Labeler.BOTTOM); } } @Deprecated // Required for backward compatibility protected void drawLabel(Graphics g, Point pt, String label) { drawLabel(g, pt, label, Labeler.RIGHT, Labeler.BOTTOM); } protected void drawLabel(Graphics g, Point pt, String label, int hAlign, int vAlign) { if (label != null) { Color labelFgColor = fgColor == null ? Color.black : fgColor; Graphics2D g2d = ((Graphics2D) g); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); Labeler.drawLabel(g, label, pt.x, pt.y, new Font("Dialog", Font.PLAIN, fontSize), hAlign, vAlign, labelFgColor, bgColor, labelFgColor); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); } } protected void showDetails() { displayablePieces = getDisplayablePieces(); /* * Visibility Rules: Stack - Depends on setting of showGraphics/showText * Single Unit - Depends on setting of showGraphics/showText and * showGraphicsSingle/showTextSingle and stack must not be expanded. Empty * space - Depends on setting of */ double zoom = getZoom(); if (displayablePieces.size() < minimumDisplayablePieces) { if (displayablePieces.size() > 0) { graphicsVisible = zoom < zoomLevel; textVisible = zoom < zoomLevel && (summaryReportFormat.getFormat().length() > 0 || counterReportFormat.getFormat().length() > 0); } else { textVisible = (minimumDisplayablePieces==0 && emptyHexReportFormat.getFormat().length() > 0); graphicsVisible = false; } } else { graphicsVisible = drawPieces; textVisible = showText && (summaryReportFormat.getFormat().length() > 0 || counterReportFormat.getFormat().length() > 0); } map.repaint(); } protected double getZoom() { return map.getZoom(); } /** * Build an ArrayList of pieces to be displayed in order from bottom up, based * on selection criteria setup in config. */ protected List<GamePiece> getDisplayablePieces() { GamePiece[] allPieces = map.getPieces(); // All pieces from bottom up Visitor visitor = new Visitor(new Filter(), map, map.mapCoordinates(currentMousePosition.getPoint())); DeckVisitorDispatcher dispatcher = new DeckVisitorDispatcher(visitor); /* * Process pieces from the top down to make it easier to check for top layer * only. */ for (int i = allPieces.length - 1; i >= 0; i--) { dispatcher.accept(allPieces[i]); } return visitor.getPieces(); } /** * Utility class to select the pieces we wish to view. */ protected class Filter implements PieceFilter { protected int topLayer; public Filter() { topLayer = -1; } public boolean accept(GamePiece piece) { return accept(piece, 0, ""); } public boolean accept(GamePiece piece, int layer, String layerName) { // Is it visible to us? if (Boolean.TRUE.equals(piece.getProperty(Properties.INVISIBLE_TO_ME))) { return false; } // If it Does Not Stack, do we want to see it? if (Boolean.TRUE.equals(piece.getProperty(Properties.NO_STACK)) && !showNoStack) { return false; } if (Boolean.TRUE.equals(piece.getProperty(Properties.NON_MOVABLE)) && !showNonMovable) { return false; } if (Boolean.TRUE.equals(piece.getProperty(Properties.TERRAIN)) && !showMoveSelected) { return false; } // Deck? if (piece.getParent() instanceof Deck && !showDeck) { return false; } // Select by property filter if (displayWhat.equals(FILTER)) { return propertyFilter.accept(piece); } // Looking at All Layers accepts anything. else if (displayWhat.equals(ALL_LAYERS)) { return true; } else { if (topLayer < 0) { topLayer = layer; } // Pieces are passed to us top down, so only display the top-most layer if (displayWhat.equals(TOP_LAYER)) { return layer == topLayer; } // Include pieces on named layers only else if (displayWhat.equals(INC_LAYERS)) { for (int i = 0; i < displayLayers.length; i++) { if (layerName.equals(displayLayers[i])) { return true; } } } // Exclude pieces from named layers. else if (displayWhat.equals(EXC_LAYERS)) { for (int i = 0; i < displayLayers.length; i++) { if (layerName.equals(displayLayers[i])) { return false; } } return true; } } // Ignore anything else return false; } } /* * Utility class to visit Map pieces, apply the filter and return a list of * pieces we are interested in. */ protected static class Visitor extends PieceFinder.Movable { protected List<GamePiece> pieces; protected Filter filter = null; protected CompoundPieceCollection collection; protected int lastLayer = -1; protected int insertPos = 0; protected Point foundPieceAt; public Visitor(Filter filter, Map map, Point pt) { super(map,pt); if (map.getPieceCollection() instanceof CompoundPieceCollection) { collection = (CompoundPieceCollection) map.getPieceCollection(); } pieces = new ArrayList<GamePiece>(); this.filter = filter; } public Object visitDeck(Deck d) { if (foundPieceAt == null) { GamePiece top = d.topPiece(); if (top != null && !Boolean.TRUE.equals(top.getProperty(Properties.OBSCURED_TO_ME))) { Rectangle r = (Rectangle) d.getShape(); r.x += d.getPosition().x; r.y += d.getPosition().y; if (r.contains(pt)) { apply(top); } } } return null; } public Object visitStack(Stack s) { boolean addContents = foundPieceAt == null ? super.visitStack(s) != null : foundPieceAt.equals(s.getPosition()); if (addContents) { for (Iterator<GamePiece> i = s.getPiecesIterator(); i.hasNext();) { apply(i.next()); } } return null; } public Object visitDefault(GamePiece p) { if (foundPieceAt == null ? super.visitDefault(p) != null : foundPieceAt.equals(p.getPosition())) { apply(p); } return null; } /* * Insert accepted pieces into the start of the array since we are being * passed pieces from the top down. */ protected void apply(GamePiece p) { int layer = 0; String layerName = ""; layer = collection.getLayerForPiece(p); layerName = collection.getLayerNameForPiece(p); if (filter == null || filter.accept(p, layer, layerName)) { if (layer != lastLayer) { insertPos = 0; lastLayer = layer; } if (foundPieceAt == null) { foundPieceAt = p.getPosition(); } pieces.add(insertPos++, p); } } public List<GamePiece> getPieces() { return pieces; } } public void mouseMoved(MouseEvent e) { // clear details when mouse moved if (graphicsVisible || textVisible) { hideDetails(); } else { currentMousePosition = e; if (Boolean.FALSE.equals( GameModule.getGameModule().getPrefs().getValue(USE_KEYBOARD))) { // Restart timer if (delayTimer.isRunning()) delayTimer.stop(); delayTimer.setInitialDelay(getPreferredDelay()); delayTimer.start(); } } } protected int getPreferredDelay() { return (Integer) GameModule.getGameModule().getPrefs().getValue(PREFERRED_DELAY); } public void mouseDragged(MouseEvent e) { mouseMoved(e); } public void mouseClicked(MouseEvent e) { } public void mouseEntered(MouseEvent e) { mouseInView = true; } public void mouseExited(MouseEvent e) { mouseInView = false; } public void mousePressed(MouseEvent e) { if (delayTimer.isRunning()) delayTimer.stop(); } public void mouseReleased(MouseEvent e) { mouseInView = true; if (delayTimer.isRunning()) delayTimer.stop(); } public void dragMouseMoved(DragSourceDragEvent e) { // This prevents the viewer from popping up during piece drags. if (delayTimer.isRunning()) delayTimer.stop(); } public void keyTyped(KeyEvent e) { } public void keyPressed(KeyEvent e) { if (hotkey != null && Boolean.TRUE.equals(GameModule.getGameModule().getPrefs().getValue(USE_KEYBOARD))) { if (hotkey.equals(KeyStroke.getKeyStrokeForEvent(e))) { showDetails(); } else { hideDetails(); } } } public void keyReleased(KeyEvent e) { } protected void hideDetails() { graphicsVisible = false; textVisible = false; map.repaint(); } /* * Compatibility. If this component has not yet been saved by this version of * vassal, convert the old-style options to new and update the version. */ public Configurer getConfigurer() { // New version 2 viewer being created if (map == null) { version = LATEST_VERSION; } // Previous version needing upgrading? else if (!version.equals(LATEST_VERSION)) { upgrade(); } return super.getConfigurer(); } protected void upgrade() { if (!drawPieces && !showText) { minimumDisplayablePieces = Integer.MAX_VALUE; } else if (drawSingleDeprecated) { minimumDisplayablePieces = 1; } else { minimumDisplayablePieces = 2; } fgColor = map.getHighlighter() instanceof ColoredBorder ? ((ColoredBorder) map.getHighlighter()).getColor() : Color.black; bgColor = new Color(255 - fgColor.getRed(), 255 - fgColor.getGreen(), 255 - fgColor.getBlue()); version = LATEST_VERSION; } public String[] getAttributeNames() { return new String[] { VERSION, DELAY, HOTKEY, BG_COLOR, FG_COLOR, MINIMUM_DISPLAYABLE, ZOOM_LEVEL, DRAW_PIECES, DRAW_PIECES_AT_ZOOM, GRAPH_SINGLE_DEPRECATED, BORDER_WIDTH, SHOW_TEXT, SHOW_TEXT_SINGLE_DEPRECATED, FONT_SIZE, SUMMARY_REPORT_FORMAT, COUNTER_REPORT_FORMAT, EMPTY_HEX_REPORT_FORMAT, DISPLAY, LAYER_LIST, PROPERTY_FILTER,SHOW_NOSTACK, SHOW_MOVE_SELECTED, SHOW_NON_MOVABLE, UNROTATE_PIECES, SHOW_DECK }; } public String[] getAttributeDescriptions() { return new String[] { Resources.getString("Editor.MouseOverStackViewer.version"), //$NON-NLS-1$ not displayed Resources.getString("Editor.MouseOverStackViewer.recommend_delay"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.keyboard_shortcut"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.bg_color"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.text_color"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.display_pieces"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.display_zoom"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.draw_pieces"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.draw_zoom"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.display_graphics_obselete"), //$NON-NLS-1$ Obsolete Resources.getString("Editor.MouseOverStackViewer.piece_gap"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.display_text"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.display_text_obsolete"), //$NON-NLS-1$ Obsolete Resources.getString("Editor.MouseOverStackViewer.font_size"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.summary_text"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.text_below"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.text_empty"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.include_pieces"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.listed_layers"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.piece_filter"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.non_stacking"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.move_selected"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.non_moveable"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.unrotated_state"), //$NON-NLS-1$ Resources.getString("Editor.MouseOverStackViewer.top_deck"), //$NON-NLS-1$ }; } public Class<?>[] getAttributeTypes() { return new Class<?>[] { String.class, Integer.class, KeyStroke.class, Color.class, Color.class, MinConfig.class, Double.class, Boolean.class, Double.class, Boolean.class, Integer.class, Boolean.class, Boolean.class, Integer.class, ReportFormatConfig.class, CounterFormatConfig.class, EmptyFormatConfig.class, DisplayConfig.class, String[].class, PropertyExpression.class, Boolean.class, Boolean.class, Boolean.class, Boolean.class, Boolean.class }; } public static class DisplayConfig extends StringEnum { public String[] getValidValues(AutoConfigurable target) { return new String[] {TOP_LAYER, ALL_LAYERS, INC_LAYERS, EXC_LAYERS, FILTER}; } } public static class MinConfig extends StringEnum { public String[] getValidValues(AutoConfigurable target) { return new String[] {"0", "1", "2"}; } } public static class EmptyFormatConfig implements ConfigurerFactory { public Configurer getConfigurer(AutoConfigurable c, String key, String name) { return new FormattedStringConfigurer(key, name, new String[] {BasicPiece.LOCATION_NAME, BasicPiece.CURRENT_MAP, BasicPiece.CURRENT_BOARD, BasicPiece.CURRENT_ZONE}); } } public static class ReportFormatConfig implements ConfigurerFactory { public Configurer getConfigurer(AutoConfigurable c, String key, String name) { return new FormattedStringConfigurer(key, name, new String[] {BasicPiece.LOCATION_NAME, BasicPiece.CURRENT_MAP, BasicPiece.CURRENT_BOARD, BasicPiece.CURRENT_ZONE, SUM}); } } public static class CounterFormatConfig implements ConfigurerFactory { public Configurer getConfigurer(AutoConfigurable c, String key, String name) { return new FormattedStringConfigurer(key, name, new String[] {BasicPiece.PIECE_NAME}); } } public Class<?>[] getAllowableConfigureComponents() { return new Class<?>[0]; } public HelpFile getHelpFile() { return HelpFile.getReferenceManualPage("Map.htm", "StackViewer"); } public void removeFrom(Buildable parent) { map.removeDrawComponent(this); view.removeMouseMotionListener(this); } public void setAttribute(String name, Object value) { if (DELAY.equals(name)) { if (value instanceof String) { value = Integer.valueOf((String) value); } if (value != null) { delay = ((Integer) value).intValue(); } } else if (HOTKEY.equals(name)) { if (value instanceof String) { hotkey = HotKeyConfigurer.decode((String)value); } else { hotkey = (KeyStroke)value; } } else if (DRAW_PIECES.equals(name)) { if (value instanceof Boolean) { drawPieces = ((Boolean) value).booleanValue(); } else if (value instanceof String) { drawPieces = "true".equals(value); } } else if (GRAPH_SINGLE_DEPRECATED.equals(name)) { if (value instanceof Boolean) { drawSingleDeprecated = ((Boolean) value).booleanValue(); } else if (value instanceof String) { drawSingleDeprecated = "true".equals(value); } } else if (SHOW_TEXT.equals(name)) { if (value instanceof Boolean) { showText = ((Boolean) value).booleanValue(); } else if (value instanceof String) { showText = "true".equals(value); } } else if (SHOW_TEXT_SINGLE_DEPRECATED.equals(name)) { if (value instanceof Boolean) { showTextSingleDeprecated = ((Boolean) value).booleanValue(); } else if (value instanceof String) { showTextSingleDeprecated = "true".equals(value); } } else if (ZOOM_LEVEL.equals(name)) { if (value instanceof String) { value = Double.valueOf((String) value); } zoomLevel = ((Double) value).doubleValue(); } else if (DRAW_PIECES_AT_ZOOM.equals(name)) { if (value instanceof String) { value = Double.valueOf((String) value); } graphicsZoomLevel = ((Double) value).doubleValue(); } else if (BORDER_WIDTH.equals(name)) { if (value instanceof String) { value = Integer.valueOf((String) value); } borderWidth = ((Integer) value).intValue(); } else if (SHOW_NOSTACK.equals(name)) { if (value instanceof Boolean) { showNoStack = ((Boolean) value).booleanValue(); } else if (value instanceof String) { showNoStack = "true".equals(value); } } else if (SHOW_MOVE_SELECTED.equals(name)) { if (value instanceof Boolean) { showMoveSelected = ((Boolean) value).booleanValue(); } else if (value instanceof String) { showMoveSelected = "true".equals(value); } } else if (SHOW_NON_MOVABLE.equals(name)) { if (value instanceof Boolean) { showNonMovable = ((Boolean) value).booleanValue(); } else if (value instanceof String) { showNonMovable = "true".equals(value); } } else if (SHOW_DECK.equals(name)) { if (value instanceof Boolean) { showDeck = ((Boolean) value).booleanValue(); } else if (value instanceof String) { showDeck = "true".equals(value); } } else if (UNROTATE_PIECES.equals(name)) { if (value instanceof Boolean) { unrotatePieces = ((Boolean) value).booleanValue(); } else if (value instanceof String) { unrotatePieces = "true".equals(value); } } else if (DISPLAY.equals(name)) { displayWhat = (String) value; } else if (LAYER_LIST.equals(name)) { if (value instanceof String) { value = StringArrayConfigurer.stringToArray((String) value); } displayLayers = (String[]) value; } else if (EMPTY_HEX_REPORT_FORMAT.equals(name)) { emptyHexReportFormat.setFormat((String) value); } else if (SUMMARY_REPORT_FORMAT.equals(name)) { summaryReportFormat.setFormat((String) value); } else if (COUNTER_REPORT_FORMAT.equals(name)) { counterReportFormat.setFormat((String) value); } else if (MINIMUM_DISPLAYABLE.equals(name)) { try { minimumDisplayablePieces = Integer.parseInt((String) value); } catch (NumberFormatException e) { throw new IllegalBuildException(e); } } else if (VERSION.equals(name)) { version = (String) value; } else if (FG_COLOR.equals(name)) { if (value instanceof String) { value = ColorConfigurer.stringToColor((String) value); } fgColor = value == null ? Color.black : (Color) value; } else if (BG_COLOR.equals(name)) { if (value instanceof String) { value = ColorConfigurer.stringToColor((String) value); } bgColor = (Color) value; } else if (FONT_SIZE.equals(name)) { if (value instanceof String) { value = Integer.valueOf((String) value); } if (value != null) { fontSize = ((Integer) value).intValue(); } } else if (PROPERTY_FILTER.equals(name)) { propertyFilter.setExpression((String) value); } } public String getAttributeValueString(String name) { if (DELAY.equals(name)) { return String.valueOf(delay); } else if (HOTKEY.equals(name)) { return HotKeyConfigurer.encode(hotkey); } else if (DRAW_PIECES.equals(name)) { return String.valueOf(drawPieces); } else if (GRAPH_SINGLE_DEPRECATED.equals(name)) { return String.valueOf(drawSingleDeprecated); } else if (SHOW_TEXT.equals(name)) { return String.valueOf(showText); } else if (SHOW_TEXT_SINGLE_DEPRECATED.equals(name)) { return String.valueOf(showTextSingleDeprecated); } else if (ZOOM_LEVEL.equals(name)) { return String.valueOf(zoomLevel); } else if (DRAW_PIECES_AT_ZOOM.equals(name)) { return String.valueOf(graphicsZoomLevel); } else if (BORDER_WIDTH.equals(name)) { return String.valueOf(borderWidth); } else if (SHOW_NOSTACK.equals(name)) { return String.valueOf(showNoStack); } else if (SHOW_MOVE_SELECTED.equals(name)) { return String.valueOf(showMoveSelected); } else if (SHOW_NON_MOVABLE.equals(name)) { return String.valueOf(showNonMovable); } else if (SHOW_DECK.equals(name)) { return String.valueOf(showDeck); } else if (UNROTATE_PIECES.equals(name)) { return String.valueOf(unrotatePieces); } else if (DISPLAY.equals(name)) { return displayWhat; } else if (LAYER_LIST.equals(name)) { return StringArrayConfigurer.arrayToString(displayLayers); } else if (EMPTY_HEX_REPORT_FORMAT.equals(name)) { return emptyHexReportFormat.getFormat(); } else if (SUMMARY_REPORT_FORMAT.equals(name)) { return summaryReportFormat.getFormat(); } else if (COUNTER_REPORT_FORMAT.equals(name)) { return counterReportFormat.getFormat(); } else if (MINIMUM_DISPLAYABLE.equals(name)) { return String.valueOf(minimumDisplayablePieces); } else if (VERSION.equals(name)) { return version; } else if (FG_COLOR.equals(name)) { return ColorConfigurer.colorToString(fgColor); } else if (BG_COLOR.equals(name)) { return ColorConfigurer.colorToString(bgColor); } else if (FONT_SIZE.equals(name)) { return String.valueOf(fontSize); } else if (PROPERTY_FILTER.equals(name)) { return propertyFilter.getExpression(); } else return null; } public static String getConfigureTypeName() { return Resources.getString("Editor.MouseOverStackViewer.component_type"); //$NON-NLS-1$ } public VisibilityCondition getAttributeVisibility(String name) { if (BORDER_WIDTH.equals(name) || DRAW_PIECES_AT_ZOOM.equals(name)) { return new VisibilityCondition() { public boolean shouldBeVisible() { return drawPieces; } }; } else if (FONT_SIZE.equals(name) || SUMMARY_REPORT_FORMAT.equals(name) || COUNTER_REPORT_FORMAT.equals(name)) { return new VisibilityCondition() { public boolean shouldBeVisible() { return showText; } }; } else if (DRAW_PIECES.equals(name) || SHOW_TEXT.equals(name) || SHOW_NOSTACK.equals(name) || SHOW_DECK.equals(name) || DISPLAY.equals(name)) { return new VisibilityCondition() { public boolean shouldBeVisible() { return true; } }; } else if (LAYER_LIST.equals(name)) { return new VisibilityCondition() { public boolean shouldBeVisible() { return (displayWhat.equals(INC_LAYERS) || displayWhat.equals(EXC_LAYERS)); } }; } else if (PROPERTY_FILTER.equals(name)) { return new VisibilityCondition() { public boolean shouldBeVisible() { return displayWhat.equals(FILTER); } }; } else if (EMPTY_HEX_REPORT_FORMAT.equals(name)) { return new VisibilityCondition() { public boolean shouldBeVisible() { return showText && minimumDisplayablePieces == 0; } }; } else if (SHOW_MOVE_SELECTED.equals(name) || SHOW_NON_MOVABLE.equals(name)) { return new VisibilityCondition() { public boolean shouldBeVisible() { return showNoStack; } }; } /* * The following fields are not to be displayed. They are either obsolete * or maintained for backward compatibility */ else if (VERSION.equals(name) || SHOW_TEXT_SINGLE_DEPRECATED.equals(name) || GRAPH_SINGLE_DEPRECATED.equals(name)) { return new VisibilityCondition() { public boolean shouldBeVisible() { return false; } }; } return null; } }