/******************************* Copyright notice ******************************* Copyright (C)2002 by Andrei Loskutov <loskutov@gmx.de>. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ********************************************************************************/ package adiag.gui; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Event; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridLayout; import java.awt.Point; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.event.ActionEvent; 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.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import javax.swing.AbstractButton; import javax.swing.Action; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JEditorPane; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.JSlider; import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.ToolTipManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import Acme.JPM.Encoders.GifEncoder; import adiag.Const; import adiag.connection.Connector; import adiag.connection.P4CommandConnector; import adiag.model.Version; import adiag.util.Toolz; import adiag.view.Layout; import adiag.view.SimpleLayout; import adiag.view.VersionView; /** * The "main" view panel class, with all versions etc. * @author Andy the Great */ public class ViewPanel extends JComponent { /** used Cursor - Objekt */ public static final Cursor CURSOR_WAIT = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR); /** used Cursor - Objekt */ public static final Cursor CURSOR_DEFAULT = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR); /** used Cursor - Objekt */ public static final Cursor CURSOR_MOVE = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR); /** used Cursor - Objekt */ public static final Cursor CURSOR_HAND = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); /** used Cursor - Objekt */ public static final Cursor CURSOR_CROSSHAIR = Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR); /** last clicked version */ private VersionView clicked; /** current second clicked version, for diff */ private VersionView diffView_two; /** current first clicked version, for diff */ private VersionView diffView; /** Ctrl - key was pressed */ private boolean ctrl_key; /** Shift - key was pressed */ private boolean shift_key; /** versions to move/moved */ private VersionView[] moved; /** menu for selected version */ protected JPopupMenu popupEdit; /** menu for background click */ protected JPopupMenu popupView; /** last x, y mouse-Coordinates by click */ private int prevx; /** last x, y mouse-Coordinates by click */ private int prevy; /** last x, y mouse-Coordinates by drag */ private int drag_x; /** last x, y mouse-Coordinates by drag */ private int drag_y; /** if we even change zoom from 1 to +- */ private boolean image_scaled; /** x - scale factor for view */ private double scale_x_axis = 1; /** y - scale factor for view */ private double scale_y_axis = 1; /** painting context size */ private Dimension backSize; /** the instance of layout component */ private Layout layout; /** all known actions */ private HashMap actions = new HashMap(13); /** my window instance */ private App app; /** one of menu names */ private static final String COMMAND_EXPLORE_ONE_BRANCH = "get info's for this branch"; /** one of menu names */ private static final String COMMAND_EXPLORE_ALL_BRANCHES = "get info's over all branches"; /** one of menu names */ private static final String COMMAND_CHANGE_FILE = "view another depot file..."; /** one of menu names */ private static final String COMMAND_CHANGE_LOGFILE = "load view from logfile..."; /** one of menu names */ private static final String COMMAND_REDO_LAYOUT = "redo layout"; /** one of menu names */ private static final String COMMAND_SAVE_AS_IMAGE = "save as image(s) + html..."; /** one of menu names */ private static final String COMMAND_SET_PROPS = "set properties..."; /** one of menu names */ private static final String COMMAND_ABOUT = "about"; /** one of menu names */ private static final String COMMAND_HELP = "help"; /** one of menu names */ private static final String COMMAND_DIFF = "diff to..."; /** one of menu names */ private static final String COMMAND_COPY = "copy path to clipboard"; /** one of menu names */ private static final String COMMAND_COPY_DATA = "copy data to clipboard"; /** one of menu names */ private static final String COMMAND_SAVE_AS_TEXT= "save data to file..."; /** one of menu names */ private static final String COMMAND_SWITCH_ON_OFF_LINE= "switch on/offline"; /** one of menu names */ private static final String COMMAND_NEW_WINDOW= "clone view / open new window"; /** one of menu names */ private static final String COMMAND_COLLAPSE_ALL = "collapse/expand all branches"; /** one of menu names */ private static final String COMMAND_COLLAPSE_BRANCH = "collapse/expand this branch"; /** class to create menus */ private MenuCreator menuCreator; /** panel to show/view options */ private PropertiesPanel props_pnl; /** offscreen image */ private BufferedImage backBuffer; /** painting context */ private Graphics2D backGC; /** flag to redraw view */ protected boolean need_re_paint; /** flag, to create new offscreen buffer */ protected boolean need_new_buffer; /** flag, if we change offscreen location by zoom */ protected boolean offscreen_location_scaled; /** used by error handling with offscreen buffer */ protected boolean error_by_make_buffer; /** used by error handling with offscreen buffer */ private int h_size_dividor = 1; /** curent screen size */ private Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); /** Really existing image size for offscreen painting, it may be smaller then * really needed size for layout all information. */ private Dimension offscreenSize = new Dimension(screenSize.width*2, screenSize.height*2); /** position of left upper corner of offscreen image in entire layout space */ private Point offscreenLocation = new Point(0,0); /** * Default Constructor */ public ViewPanel() { super(); addKeyListener(new KeyServer()); setDoubleBuffered(false); backSize = new Dimension(100, 100); this.setBackground(Color.white); this.setForeground(Color.black); ToolTipManager t = ToolTipManager.sharedInstance(); t.setDismissDelay(1000000); addMouseListener(t); addMouseMotionListener(t); menuCreator = new MenuCreator(); popupView = menuCreator.createViewPopup(); popupEdit = menuCreator.createEditPopup(); addMouseListener(new MouseListenerServer()); addMouseMotionListener(new MouseMotionServer()); setRequestFocusEnabled(true); //setAutoscrolls(true); //requestFocus(); } /** * Init layout too * @param versions die zu zeichnende Versionen * @param app akuelle Fensterinstanz */ public ViewPanel(Version[] versions, App app) { this(); this.app = app; layout = new SimpleLayout(versions, this); refreshLayout(false); } /** * @see Component#setCursor(Cursor) */ public void setCursor(Cursor c) { if (!this.getCursor().equals(c)) { super.setCursor(c); } } /** Check, if given event the 'popup'- event on operation system * (i.e.: Linux:pressed, Windows:released), and shows then context-sensitive menu * (or not ;)). * @param e MouseEvent */ private void maybeShowPopup(MouseEvent e) { int x, y; if (e.isPopupTrigger()) { x = e.getX(); y = e.getY(); Action a; // click on revision if (clicked != null) { Version v = clicked.getVersion(); a = (Action) actions.get(COMMAND_EXPLORE_ONE_BRANCH); if (v != null && (!v.isReceived() || !v.isChangeNumOk()) && !app.isOffline()) { if (a != null) { a.setEnabled(true); } } else { if (a != null) { a.setEnabled(false); } } a = (Action) actions.get(COMMAND_DIFF); if (v != null && !Const.TYPE_DELETE.equals(v.getChangeType()) && !app.isOffline()) { if (a != null) { a.setEnabled(true); } } else { if (a != null) { a.setEnabled(false); } } popupEdit.show(this, x, y); } // click on background else { String[] ubr = app.getConnector().getUnknownBranches(true); a = (Action) actions.get(COMMAND_EXPLORE_ALL_BRANCHES); if (ubr != null && ubr.length > 0 && !app.isOffline()) { if (a != null) { a.setEnabled(true); } } else { if (a != null) { a.setEnabled(false); } } popupView.show(this, x, y); } } } /** See return description * @return Dimension dimension of offscreen image * @see Component#getPreferredSize() */ public Dimension getPreferredSize() { if (backSize == null) { backSize = new Dimension(100, 100); return backSize; } else { Rectangle r = layout.getBounds(); return new Dimension((int) (r.getWidth() * scale_x_axis), (int) (r.getHeight() * scale_y_axis)); } } /** * Overriden, see return description * @return always true * @see JComponent#isOptimizedDrawingEnabled() */ public boolean isOptimizedDrawingEnabled() { return true; } /** * Overriden, see return description * @return always true * @see Component#isOpaque() */ public boolean isOpaque() { return true; } /** * Overriden to paint custom component content * @see JComponent#paintComponent(Graphics) * @param g painting context */ public void paintComponent(Graphics g) { if (g != null) { if (layout != null) { if (need_re_paint) { if (backGC != null) { if(scale_x_axis != 0 && scale_y_axis != 0){ backGC.clearRect(-offscreenLocation.x, -offscreenLocation.y, - (int)((offscreenLocation.x - offscreenSize.width)/scale_x_axis), - (int)((offscreenLocation.y - offscreenSize.height)/scale_y_axis)); } } else { newBackBuffer(offscreenSize); } this.need_re_paint = false; this.need_new_buffer = false; layout.paint(backGC); } Point p = getRightLocation(); Graphics2D gr = (Graphics2D) g; gr.setBackground(Color.white); gr.clearRect(p.x, p.y, offscreenSize.width, offscreenSize.height); gr.drawImage(backBuffer, p.x, p.y, this); } } } /** * Create new offscreen image * @param r new offscreen dimensions */ private void newBackBuffer(Dimension r) { Graphics2D old_backGC = backGC; BufferedImage new_backBuffer = null; Graphics2D new_backGC = null; // we can have OutOfMemoryError here, if image size is too large... try { new_backBuffer = new BufferedImage(r.width, r.height, BufferedImage.TYPE_USHORT_565_RGB); new_backGC = (Graphics2D) new_backBuffer.getGraphics(); new_backGC.setBackground(Color.white); new_backGC.clearRect(0, 0, r.width, r.height); backSize = new Dimension(r.width, r.height); backGC = new_backGC; backBuffer = new_backBuffer; if (old_backGC != null) { old_backGC.dispose(); } // refresh point of view checkAndMoveOffscreen(); System.runFinalization(); System.gc(); if (error_by_make_buffer) { setPreferredSize(new Dimension(r.width, r.height)); } h_size_dividor = 1; error_by_make_buffer = false; Const.H_SPACE = Const.H_SPACE_D; } catch (Throwable e) { h_size_dividor++; error_by_make_buffer = true; e.printStackTrace(); if (old_backGC != null) { old_backGC.dispose(); old_backGC = null; } if (new_backGC != null) { new_backGC.dispose(); new_backGC = null; } System.runFinalization(); System.gc(); if(!error_by_make_buffer){ // first time Action a = (Action) actions.get(COMMAND_COLLAPSE_ALL); a.actionPerformed(new ActionEvent(this, -1, COMMAND_COLLAPSE_ALL)); } if (h_size_dividor < 3) { JOptionPane.showMessageDialog( app, "\nImage size is too large, sorry", "View: exception occured during creating new buffer for image!", JOptionPane.ERROR_MESSAGE); Const.H_SPACE = Const.V_SPACE; layout.doLayout(); Dimension r2 = screenSize;//layout.getBounds(); this.backSize = new Dimension(r2.width, r2.height); newBackBuffer(r2); } } } /** Overriden * @param x the 0 - coordinate on the x - axis * @param y the 0 - coordinate on the y - axis * @see Component#setLocation(int, int) */ public void setLocation(int x, int y) { super.setLocation(x, y); // -xxx , -yyy //System.out.println("new location to x:" + x + ", y:" + y); //Rectangle r = new Rectangle(x, y, 100, 100); checkAndMoveOffscreen(); //if(true){ //System.out.println("view position before:" + scrollComponent.getViewport().getViewPosition()); //scrollComponent.getViewport().scrollRectToVisible(r); //System.out.println("view position after:" + scrollComponent.getViewport().getViewPosition()); //} } /** * Check, if we need to move current offscreen position to the next one, * and then do this. * @return boolean true, if screen position was changed */ private boolean checkAndMoveOffscreen() { Point nextLocation = getNextOffscreenLocation(); boolean changed = !offscreenLocation.equals(nextLocation); if(changed){ if(backGC == null){ return false; } //System.out.println("move offscreen!!!"); //int dx = nextLocation.x - offscreenLocation.x; //int dy = nextLocation.y - offscreenLocation.y; //backGC.translate(dx, dy); backGC.translate(-offscreenLocation.x, -offscreenLocation.y); backGC.translate(nextLocation.x, nextLocation.y); this.need_re_paint = true; // store current image position offscreenLocation = nextLocation; } SwingUtilities.invokeLater(new Runnable() { public void run() { revalidate(); repaint(); } }); return changed; } /** * See return description * @return Point with new calculated "viewable window" on offscreen position, * based on current location of panel in scrollpane. This may be old position, * if viewable piece fit to offscreen window position, or new position, never null. */ private Point getNextOffscreenLocation(){ // if 'layout size' fit to screen, do nothing // preferred is already multiplied with zoom Dimension d = getPreferredSize(); if(d.width <= offscreenSize.width && d.height<= offscreenSize.height){ return new Point(0,0); } // 1) we have back screen, witch 2x bigger as size of screenSize // and screenSize is maximum of 'visible' screen size // if visible position + window height(width) is over this back screen // lower(upper) bounds, we need redraw back screen Point upp_left_vis = getLocation(); Rectangle visible_loc = getVisibleRect(); // visible is not multiplied with zoom, also we must change it? Point low_right_vis = new Point ( upp_left_vis.x - visible_loc.width , upp_left_vis.y - visible_loc.height); // real point Point low_right_hidd = new Point ( offscreenLocation.x - offscreenSize.width, offscreenLocation.y - offscreenSize.height); Point result = new Point(offscreenLocation); boolean changed =false;//offscreen_location_scaled; int dx = (int)(offscreenSize.width * 0.5); int dy = (int)(offscreenSize.height * 0.5); // x coordinate first if(upp_left_vis.x > offscreenLocation.x){ result.x = offscreenLocation.x + dx; changed = true; } else if(low_right_vis.x < low_right_hidd.x){ result.x = offscreenLocation.x - dx; changed = true; } // y coordinate if(upp_left_vis.y > offscreenLocation.y){ result.y = offscreenLocation.y + dy; changed = true; } else if(low_right_vis.y < low_right_hidd.y){ result.y = offscreenLocation.y - dy; changed = true; } if(changed){ //System.out.println("***need repaint:" + result); offscreen_location_scaled = false; return result; } else { return offscreenLocation; } } /** * See return description * @return Point position on paint context with offscreen corrections */ private Point getRightLocation(){ Point current_vis = new Point(getLocation()); current_vis.x = (int)(current_vis.x - offscreenLocation.x); current_vis.y = (int)(current_vis.y - offscreenLocation.y); return current_vis; } /** Overriden * @param p New position * @see Component#setLocation(Point) */ public void setLocation(Point p) { this.setLocation(p.x, p.y); } /** * Request new data for given version from P4 process * @param v Version */ private void exploreVersion(Version v) { Connector c = app.getConnector(); app.setPath(v.getPath()); try { layout.addVersions(c.getData()); refreshLayout(false); } catch (Exception e) { app.error("Cannot get info:\n" + e.getMessage(), e); } } /** * Request new data for all known branches from P4 process */ private void exploreAllVersions() { Connector c = app.getConnector(); app.setDiscoverAllBranches(true); try { layout.addVersions(c.getData()); refreshLayout(false); } catch (Exception e) { app.error("Cannot get info:\n" + e.getMessage(), e); } app.setDiscoverAllBranches(false); } /** * Update view and redo layout * @param changeOffscreenLocation currently not used */ public void refreshLayout(boolean changeOffscreenLocation) { if (error_by_make_buffer) { h_size_dividor = 1; Const.H_SPACE = Const.H_SPACE_D; error_by_make_buffer = false; } layout.doLayout(); Rectangle r = layout.getBounds(); Dimension d = new Dimension(r.width, r.height); /*if(changeOffscreenLocation){ this.offscreen_location_scaled = false; if(backGC != null){ backGC.translate(-offscreenLocation.x, -offscreenLocation.y); } this.offscreenLocation = new Point(0,0); this.setLocation(0,0); }*/ setPreferredSize(d); app.setBestAppSizeAndLocation(d); repaint(); } /** Overriden * @param d new dimensions * @see JComponent#setPreferredSize(Dimension) */ public void setPreferredSize(Dimension d) { backSize = d; need_re_paint = true; //need_new_buffer = true; checkAndMoveOffscreen(); revalidate(); repaint(); } /** * See return description * @return String extended version information, if available * @param e MouseEvent with current mouse coordinates */ public String getToolTipText(MouseEvent e) { // if in getBounds() setLocation not used!!! //Point p = getLocation(); int x = e.getX();// + p.x; int y = e.getY();// + p.y; int[] xy = fromScreenToLayout(x, y); String text = layout.getInfo(xy[0], xy[1]); return text; } /** * Convert view coordinates to the layout - coordinates * @return int[] the x, y Layout - coordinates * @param x int the x - coordinate from view * @param y int the y - coordinate from view */ private int[] fromScreenToLayout(int x, int y) { if (scale_x_axis > 0) { //x = (int) (x / scale_x_axis); x = (int)((x + offscreenLocation.x) / scale_x_axis - offscreenLocation.x); } if (scale_y_axis > 0) { //y = (int) (y / scale_y_axis); y = (int)((y + offscreenLocation.y) / scale_y_axis - offscreenLocation.y); } return new int[] { x, y}; } /** * Convert layout coordinates to the view - coordinates * @return int[] the x, y screen - coordinates * @param x int the x - coordinate from layout * @param y int the y - coordinate from layout */ private int[] fromLayoutToScreen(double x, double y) { if (scale_x_axis > 0) { x = ((x + offscreenLocation.x) * scale_x_axis - offscreenLocation.x); } if (scale_y_axis > 0) { y = ((y + offscreenLocation.y) * scale_y_axis - offscreenLocation.y); } return new int[] { (int)x, (int)y}; } /** * Set the skale faktor on the x - axis * @param newScale_x_axis float the new skale faktor on the x - axis */ public void setScale_x_axis(double newScale_x_axis) { if (newScale_x_axis > 0){ if(backGC != null && scale_x_axis != 0){ double sx = newScale_x_axis / scale_x_axis; if(sx != 1){ backGC.translate(-offscreenLocation.x,-offscreenLocation.y); backGC.scale(sx, 1); backGC.translate(offscreenLocation.x, offscreenLocation.y); scale_x_axis *= sx; offscreen_location_scaled = true; } } } } /** * Set the skale faktor on the y - axis * @param newScale_y_axis float the new skale faktor on the y - axis */ public void setScale_y_axis(double newScale_y_axis) { if (newScale_y_axis > 0){ if(backGC != null && scale_y_axis != 0){ double sy = newScale_y_axis / scale_y_axis; if(sy != 1){ backGC.translate(-offscreenLocation.x,-offscreenLocation.y); backGC.scale(1, sy); backGC.translate(offscreenLocation.x, offscreenLocation.y); scale_y_axis *= sy; offscreen_location_scaled = true; } } } } /** * Zoom in/out * @param sx scale factor on the x axis * @param sy scale factor on the y axis */ private void zoom(double sx, double sy){ //Point p = getLocation(); //System.out.println("zoom: get location before:" + p); // first, we need change here location point (anchor) if needed setScale_x_axis(sx); setScale_y_axis(sy); //setLocation((int)(offscreenLocation.x * scale_x_axis), (int)(offscreenLocation.y * scale_y_axis)); //Rectangle r = new Rectangle(-p.x, -p.y, 100, 100); need_re_paint = true; // second, we need move offscreen, if location was changed checkAndMoveOffscreen(); //if(true){ //System.out.println("view position before:" + scrollComponent.getViewport().getViewPosition()); //scrollComponent.getViewport().scrollRectToVisible(r); //System.out.println("view position after:" + scrollComponent.getViewport().getViewPosition()); //} //System.out.println("zoom: get location after:" + getLocation()); } /** * Change path of the view to new one * @param path P4 depot path to view */ private void changeFile(final String path) { app.getPropsSetter().setProperty(Const.PROPERTY_OFFLINE, "false"); Connector c = new P4CommandConnector(app); app.setConnector(c); app.setOffline(false); app.setPath(path); try { layout.setVersions(c.getData()); refreshLayout(true); } catch (Exception ee) { app.error("Cannot change file:\n" + ee.getMessage(), ee); } } /** * Panel to show/set options * @author Andy the Great */ protected class PropertiesPanel extends JPanel { /** table, key is option, value is textbox */ HashMap text_map = new HashMap(); /** * Constructor for PropertiesPanel. */ public PropertiesPanel() { super(); App.PropertySetter setter = app.getPropsSetter(); ArrayList keys = setter.getKeyList(); this.setLayout(new GridLayout(keys.size(), 2, 3, 3)); String key, value; JTextField t; JLabel l; for (int i = 0; i < keys.size(); i++) { key = "" + keys.get(i); value = "" + setter.getProperty(key); t = new JTextField(value); t.setName(key); text_map.put(key, t); l = new JLabel(key); l.setName(key); this.add(l); this.add(t); } } /** * set properties for application */ public void setProps() { App.PropertySetter setter = app.getPropsSetter(); ArrayList keys = setter.getKeyList(); String key, value; for (int i = 0; i < keys.size(); i++) { key = "" + keys.get(i); value = "" + ((JTextField) text_map.get(key)).getText().trim(); if (!setter.getProperty(key).trim().equals(value)) { setter.setProperty(key, value); } } } /** * Set values in this panel from current values from application */ public void reset() { JTextField t; App.PropertySetter setter = app.getPropsSetter(); ArrayList alist = new ArrayList(text_map.keySet()); for (int i = 0; i < alist.size(); i++) { t = (JTextField) text_map.get(alist.get(i)); t.setText(setter.getProperty(t.getName())); } } } /** * Class to administrate key input * @author Andy the Great */ protected class KeyServer implements KeyListener { /** * Constructor for KeyServer. */ public KeyServer() { super(); } /** * One key was pressed... * @param e java.awt.event.KeyEvent * @see KeyListener#keyPressed(KeyEvent) */ public void keyPressed(KeyEvent e) { int keyCode = e.getKeyCode(); //char c = e.getKeyChar(); //System.out.println("pressed: key code = " + keyCode + " (" + KeyEvent.getKeyText(keyCode) + ")"); switch (keyCode) { case KeyEvent.VK_SHIFT : { shift_key = true; break; } case KeyEvent.VK_CONTROL : { ctrl_key = true; break; } } } /** * One key was released... * @param e java.awt.event.KeyEvent * @see KeyListener#keyReleased(KeyEvent) */ public void keyReleased(KeyEvent e) { int keyCode = e.getKeyCode(); char c = e.getKeyChar(); int modifiers = e.getModifiers(); //System.out.println(e.getKeyText(keyCode) + " " + e.getKeyModifiersText(modifiers)); if ((modifiers & InputEvent.CTRL_MASK) != 0 || (modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { String s = e.getKeyText(keyCode); c = s.length() == 1 ? s.charAt(0) : c; } Action a = null; switch (c) { case 'a' : case 'A' : a = (Action) actions.get(COMMAND_ABOUT); if(a.isEnabled()){ a.actionPerformed(new ActionEvent(this, -1, COMMAND_ABOUT)); } return; case 'c' : case 'C' : a = (Action) actions.get(COMMAND_COPY); if(a.isEnabled()){ a.actionPerformed(new ActionEvent(this, -1, COMMAND_COPY)); } return; case 'd' : case 'D' : a = (Action) actions.get(COMMAND_COPY_DATA); if(a.isEnabled()){ a.actionPerformed(new ActionEvent(this, -1, COMMAND_COPY_DATA)); } return; case 'e' : case 'E' : a = (Action) actions.get(COMMAND_COLLAPSE_ALL); if(a.isEnabled()){ a.actionPerformed(new ActionEvent(this, -1, COMMAND_COLLAPSE_ALL)); } return; case 'i' : case 'I' : a = (Action) actions.get(COMMAND_EXPLORE_ALL_BRANCHES); if(a.isEnabled()){ a.actionPerformed(new ActionEvent(this, -1, COMMAND_EXPLORE_ALL_BRANCHES)); } return; case 'l' : case 'L' : a = (Action) actions.get(COMMAND_CHANGE_LOGFILE); if(a.isEnabled()){ a.actionPerformed(new ActionEvent(this, -1, COMMAND_CHANGE_LOGFILE)); } return; case 'n' : case 'N' : a = (Action) actions.get(COMMAND_NEW_WINDOW); if(a.isEnabled()){ a.actionPerformed(new ActionEvent(this, -1, COMMAND_NEW_WINDOW)); } return; case 'p' : case 'P' : a = (Action) actions.get(COMMAND_SET_PROPS); if(a.isEnabled()){ a.actionPerformed(new ActionEvent(this, -1, COMMAND_SET_PROPS)); } return; case 'r' : case 'R' : a = (Action) actions.get(COMMAND_REDO_LAYOUT); if(a.isEnabled()){ a.actionPerformed(new ActionEvent(this, -1, COMMAND_REDO_LAYOUT)); } return; case 's' : case 'S' : a = (Action) actions.get(COMMAND_SAVE_AS_IMAGE); if(a.isEnabled()){ a.actionPerformed(new ActionEvent(this, -1, COMMAND_SAVE_AS_IMAGE)); } return; case 't' : case 'T' : a = (Action) actions.get(COMMAND_SAVE_AS_TEXT); if(a.isEnabled()){ a.actionPerformed(new ActionEvent(this, -1, COMMAND_SAVE_AS_TEXT)); } return; case 'v' : case 'V' : a = (Action) actions.get(COMMAND_CHANGE_FILE); if(a.isEnabled()){ a.actionPerformed(new ActionEvent(this, -1, COMMAND_CHANGE_FILE)); } return; default : break; } //System.err.println("pressed: key code = " + keyCode + " (" + KeyEvent.getKeyText(keyCode) + ")"); switch (keyCode) { case KeyEvent.VK_F1 : { a = (Action) actions.get(COMMAND_HELP); a.actionPerformed(new ActionEvent(this, -1, COMMAND_HELP)); break; } case KeyEvent.VK_SUBTRACT : case KeyEvent.VK_ADD : { int diff = keyCode == KeyEvent.VK_ADD ? +1 : -1;//+0.15f : -0.15f; if ((modifiers & InputEvent.CTRL_MASK) != 0) { menuCreator.zoom_slider.setValue(menuCreator.zoom_slider.getValue() + diff); //zoom(scale_x_axis + diff, scale_y_axis + diff); } break; } case KeyEvent.VK_SHIFT : { shift_key = false; break; } case KeyEvent.VK_CONTROL : { ctrl_key = false; break; } case KeyEvent.VK_END : { Rectangle r = getVisibleRect(); r.x = getPreferredSize().width; r.y = getPreferredSize().height; scrollRectToVisible(r); break; } case KeyEvent.VK_HOME : { Rectangle r = getVisibleRect(); r.x = 0; r.y = 0; scrollRectToVisible(r); break; } } } /** * Overriden, but not used * @param e java.awt.event.KeyEvent * @see KeyListener#keyTyped(KeyEvent) */ public void keyTyped(KeyEvent e) {} } /** * Class to administrate mouse motion * @author Andy the Great */ protected class MouseMotionServer implements MouseMotionListener { /** * Constructor for MouseMotionServer. */ public MouseMotionServer() { super(); } /** Mouse was dragged.... * @param e MouseEvent * @see MouseMotionListener#mouseDragged(MouseEvent) */ public void mouseDragged(MouseEvent e) { int x = e.getX();// + p.x; int y = e.getY();// + p.y; if (clicked != null) { moved = new VersionView[] { clicked }; if (ctrl_key && !shift_key || SwingUtilities.isRightMouseButton(e)) { moved = Toolz.getAllFromPath(clicked); } else if (ctrl_key && shift_key) { moved = (VersionView[]) layout.getElements().toArray(new VersionView[layout.getElements().size()]); } float[] min_xy = Toolz.findMin(moved); int[] xy = fromScreenToLayout(x, y); double dx = xy[0] - (clicked.getX() + prevx); // double dy = xy[1] - (clicked.getY() + prevy); // dx = dx + min_xy[0] < 0 ? 0 : dx; dy = dy + min_xy[1] < 0 ? 0 : dy; for (int i = 0; i < moved.length; i++) { moved[i].dx += dx; moved[i].dy += dy; } need_re_paint = true; repaint(); } else { int dx, dy; Rectangle r = getVisibleRect(); // hack because hacked getBounds() Point p = getLocation(); r.setLocation(-p.x, -p.y); dx = - (int) (drag_x - x); drag_x = x; dy = - (int) (drag_y - y); drag_y = y; r.x += dx / 2; r.y += dy / 2; scrollRectToVisible(r); repaint(); } } /** * Mouse was moved... * @param e MouseEvent * @see MouseMotionListener#mouseMoved(MouseEvent) */ public void mouseMoved(MouseEvent e) { //Point p = getLocation(); int x = e.getX();// + p.x; int y = e.getY();// + p.y; int[] xy = fromScreenToLayout(x, y); VersionView v = layout.getVersionView(xy[0], xy[1]); if (v != null) { if (diffView != null && diffView != null && !Const.TYPE_DELETE.equals(clicked.getVersion().getChangeType())) { setCursor(CURSOR_CROSSHAIR); } else { setCursor(CURSOR_HAND); } } else { setCursor(CURSOR_DEFAULT); } if (diffView != null) { paintImmediately(0, 0, getWidth(), getHeight()); xy = fromLayoutToScreen(diffView.getX(), diffView.getY()); getGraphics().drawLine( xy[0], xy[1], x , y ); } } } /** * Class to administrate mouse clicks * @author Andy the Great */ public class MouseListenerServer implements MouseListener { /** * Overriden from interface, not used * @param e MouseEvent * @see MouseListener#mouseEntered(MouseEvent) */ public void mouseEntered(MouseEvent e) {} /** * Overriden from interface, not used * @param e MouseEvent * @see MouseListener#mouseExited(MouseEvent) */ public void mouseExited(MouseEvent e) {} /** Mouse was pressed... * @param e MouseEvent * @see MouseListener#mousePressed(MouseEvent) */ public void mousePressed(MouseEvent e) { //Point p = getLocation(); int x = e.getX();// + p.x; int y = e.getY();// + p.y; clicked = null; moved = null; prevx = (drag_x = x); prevy = (drag_y = y); int[] xy = fromScreenToLayout(prevx, prevy); clicked = layout.getVersionView(xy[0], xy[1]); // Abweichung zwischen dem klickpunkt und der koordinate des objektes if (clicked != null) { prevx = xy[0] - (int) clicked.getX(); prevy = xy[1] - (int) clicked.getY(); } else { setCursor(CURSOR_MOVE); } maybeShowPopup(e); } /** Mouse was released... * @param e * @see MouseListener#mouseReleased(MouseEvent) */ public void mouseReleased(MouseEvent e) { if (moved != null && moved.length > 0) { Point max = layout.getMax(); float extra_width = Const.H_SPACE * 2; float extra_height = Const.V_SPACE * 2; double x, y = 0; for (int i = 0; i < moved.length; i++) { x = moved[i].getX(); y = moved[i].getY(); if (x + extra_width > max.x) { max.x = (int) (x + extra_width); layout.setMax(max); } if (y + extra_height > max.y) { max.y = (int) (y + extra_height); layout.setMax(max); } } Dimension dim = new Dimension(max.x, max.y); // - min.x - min.y if (!dim.equals(backSize)) { setPreferredSize(dim); } } else { maybeShowPopup(e); } if (clicked != null) { setCursor(CURSOR_HAND); } else { setCursor(CURSOR_DEFAULT); } } /** Mouse was clicked... * @see MouseListener#mouseClicked(MouseEvent) */ public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 1) { if (clicked == null) { setCursor(CURSOR_DEFAULT); if (diffView != null) { diffView.setSelected(false); diffView = null; need_re_paint = true; } if (diffView_two != null) { diffView_two.setSelected(false); diffView_two = null; need_re_paint = true; } } else if (!Const.TYPE_DELETE.equals(clicked.getVersion().getChangeType())) { // delete selection if (diffView != null) { if (!diffView.equals(clicked)) { try { app.getConnector().diff(diffView.getVersion(), clicked.getVersion()); } catch (Exception ee) { app.error("Cannot perform diff operation:\n" + ee.getMessage(), ee); } if (diffView_two != null) { diffView_two.setSelected(false); } diffView_two = clicked; diffView_two.setSelected(true); } else { // dieselbe version diffView.setSelected(false); diffView = null; if (diffView_two != null) { diffView_two.setSelected(false); diffView_two = null; } } need_re_paint = true; } else { // make first selected diffView = clicked; diffView.setSelected(true); need_re_paint = true; } } } else if (e.getClickCount() == 2 && clicked != null) { Version v = clicked.getVersion(); if (v != null && (!v.isReceived() || !v.isChangeNumOk()) && !app.isOffline()) { Action a = (Action) actions.get(COMMAND_EXPLORE_ONE_BRANCH); if (a != null) { a.actionPerformed(new ActionEvent(this, 0, "run it")); } } if (diffView != null) { diffView.setSelected(false); } if (diffView_two != null) { diffView_two.setSelected(false); } diffView_two = null; diffView = null; need_re_paint = true; } drag_x = 0; drag_y = 0; // workaround for menu disappear repaint(); } /** * Constructor for MouseListenerServer. */ public MouseListenerServer() { super(); } } /** * Menu creator helper * @author Andy the Great */ protected class MenuCreator { JSlider zoom_slider; /** * Create the version menu * @return JPopupMenu the version menu */ protected JPopupMenu createEditPopup() { JPopupMenu popup = new JPopupMenu(); JMenuItem mi = createCollapseOneMenu(); popup.add(mi); mi = createExploreOneMenu(); popup.add(mi); mi = createDiffMenu(); popup.add(mi); mi = createCopyMenu(); popup.add(mi); mi = createCopyDataMenu(); popup.add(mi); return popup; } /** * create the general view menu * @return JPopupMenu the general view menu */ protected JPopupMenu createViewPopup() { JPopupMenu popup = new JPopupMenu(); JMenuItem mi = createSaveAsImageMenu(); popup.add(mi); mi = createSaveAsTextMenu(); popup.add(mi); mi = createCopyDataMenu(); popup.add(mi); mi = createSetPropsMenu(); popup.add(mi); popup.add(new JSeparator()); mi = createNewWindowMenu(); popup.add(mi); mi = createChangeFileMenu(); popup.add(mi); mi = createChangeLogfileMenu(); popup.add(mi); popup.add(new JSeparator()); mi = createRedoLayoutMenu(); popup.add(mi); mi = createCollapseAllMenu(); popup.add(mi); mi = createExploreAllMenu(); popup.add(mi); popup.add(new JSeparator()); JPanel pan = createZoomMenu(); popup.add(pan); pan = createXSpaceMenu(); popup.add(pan); pan = createYSpaceMenu(); popup.add(pan); popup.add(new JSeparator()); mi = createHelpMenu(); popup.add(mi); mi = createAboutMenu(); popup.add(mi); // because of slider SwingUtilities.updateComponentTreeUI(popup); return popup; } /** * See return description * @return JMenuItem the help - menu entry */ private JMenuItem createHelpMenu() { StringBuffer sb = new StringBuffer("<html><body bgcolor='#F8FCE0'>"); sb.append("<table width=100% border=0 cellspacing=0 cellpadding=3>"); sb.append("<font size=2 face=Arial,Helvetica>"); sb.append("<tr bgcolor=#FBFDEC><td nowrap>"); sb.append(App.getGuiHelp(true)); sb.append("</td></tr>"); sb.append("</font><font size=2 face=Arial,Helvetica>"); sb.append("<tr bgcolor=#FBFDEC><td nowrap>"); sb.append(App.getParameterHelp(true)); sb.append("</td></tr></font>"); sb.append("</table></body></html>"); final JEditorPane lab = new JEditorPane("text/html", sb.toString()); lab.setPreferredSize(new Dimension(600, 400)); lab.setEditable(false); final JScrollPane scroll = new JScrollPane(lab); scroll.setPreferredSize(new Dimension(600, 400)); String command = COMMAND_HELP; JMenuItem mi = new JMenuItem(command); SimpleAction a = new SimpleAction(command) { public int getMnemonic() { return KeyEvent.VK_F1; } /** f1 */ public KeyStroke getKeyStroke() { return KeyStroke.getKeyStroke("F1"); } public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog( app, scroll, "Quick help for BranchView", JOptionPane.INFORMATION_MESSAGE, null); } }; //mi.setAction(a); mi.addActionListener(a); a.addPropertyChangeListener(new ActionChangedListener(mi)); actions.put(command, a); mi.setIcon(App.ICON); //mi.setMnemonic(a.getMnemonic()); mi.setAccelerator(a.getKeyStroke()); return mi; } /** * See return description * @return JMenuItem the aboout - menu entry */ private JMenuItem createAboutMenu() { String os = System.getProperty("os.name", "unknown OS"); String vm = System.getProperty("java.vm.version", "???"); String home = System.getProperty("user.home"); String temp = System.getProperty("java.io.tmpdir"); StringBuffer sb = new StringBuffer("<html><body bgcolor='#F8FCE0'>"); //#FBFDEC #F0F8BA sb.append("<table width=100% border=0 cellspacing=5 cellpadding=5>"); sb.append("<font face=Arial,Helvetica>"); sb.append("<tr bgcolor=#FBFDEC><td nowrap>Version:</td><td nowrap>").append(Const.BUILD_REVISION).append("</td></tr>"); sb.append("</font><font face=Arial,Helvetica>"); sb.append("<tr bgcolor=#F0F8BA><td nowrap>Build:</td><td nowrap>").append(Const.BUILD_DATE).append( "</td></tr>"); sb.append("</font><font face=Arial,Helvetica>"); sb.append("<tr bgcolor=#FBFDEC><td nowrap>Client OS:</td><td nowrap>").append(os).append( "</td></tr>"); sb.append("</font><font face=Arial,Helvetica>"); sb.append("<tr bgcolor=#F0F8BA><td nowrap>Java VM:</td><td nowrap>").append(vm).append( "</td></tr>"); sb.append("</font><font face=Arial,Helvetica>"); sb.append("<tr bgcolor=#FBFDEC><td nowrap>User home:</td><td nowrap>").append(home).append( "</td></tr>"); sb.append("</font><font face=Arial,Helvetica>"); sb.append("<tr bgcolor=#F0F8BA><td nowrap>%TEMP% dir:</td><td nowrap>").append(temp).append( "</td></tr>"); sb.append("</font><font face=Arial,Helvetica>"); sb.append("<tr bgcolor=#FBFDEC><td nowrap>Author:</td><td nowrap>Loskutov Andrei</td></tr>"); sb.append("</font><font face=Arial,Helvetica>"); sb.append("<tr bgcolor=#F0F8BA><td nowrap>Contact:</td><td nowrap>loskutov@gmx.de</td></tr>"); sb.append("</font><font face=Arial,Helvetica>"); sb.append("<tr bgcolor=#F0F8BA><td></td><td nowrap>Bug comments are welcome!</td></tr>"); sb.append("</font>"); sb.append("</table>"); final JEditorPane lab = new JEditorPane("text/html", sb.toString()); lab.setPreferredSize(new Dimension(500, 260)); lab.setEditable(false); String command = COMMAND_ABOUT; JMenuItem mi = new JMenuItem(command); SimpleAction a = new SimpleAction(command) { public int getMnemonic() { return KeyEvent.VK_A; } /** ctrl + a */ public KeyStroke getKeyStroke() { return KeyStroke.getKeyStroke(KeyEvent.VK_A, Event.CTRL_MASK); } public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog( app, lab, "About BranchView", JOptionPane.INFORMATION_MESSAGE, null); } }; mi.addActionListener(a); mi.setAccelerator(a.getKeyStroke()); mi.setIcon(App.ICON); a.addPropertyChangeListener(new ActionChangedListener(mi)); actions.put(command, a); return mi; } /** * See return description * @return JMenuItem the "change file" - menu entry */ private JMenuItem createChangeFileMenu() { String command = COMMAND_CHANGE_FILE; JMenuItem mi = new JMenuItem(command); mi.setActionCommand(command); mi.setToolTipText("select local file or receive file path from clipboard"); SimpleAction a = new SimpleAction(command) { private String path; public int getMnemonic() { return KeyEvent.VK_V; } /** ctrl + V */ public KeyStroke getKeyStroke() { return KeyStroke.getKeyStroke(KeyEvent.VK_V, Event.CTRL_MASK); } public void actionPerformed(ActionEvent e) { path = null; // received directly from keyboard and not from Menu if (e.getID() == -1) { path = Toolz.getClipboardContent(); if (Toolz.isPath(path)) { System.out.println("received clipboard content:'" + path + "'"); // may be directory? if(path.endsWith("/")){ path += "..."; } // don't change if same if(!app.isOffline() && app.getPath()!= null && app.getPath().trim().equals(path.trim())){ System.out.println("same path received, won't change..."); return; } } else { System.out.println("received clipboard content:'" + path + "' is not a valid path!"); path = null; return; } } else { String[] exts = new String[] {}; String[] desc = new String[] {}; File[] ff = Toolz.getFilesFromChooser( ".", JFileChooser.OPEN_DIALOG, exts, desc, false, JFileChooser.FILES_AND_DIRECTORIES, app); if (ff.length <= 0) { return; } File f = ff[0]; if (f == null) { return; } path = f.getAbsolutePath(); if (f.isDirectory()) { path += "..."; } } if (path != null) { changeFile(path); } } }; mi.addActionListener(a); a.addPropertyChangeListener(new ActionChangedListener(mi)); actions.put(command, a); //mi.setActionCommand(command); //mi.setMnemonic(a.getMnemonic()); mi.setAccelerator(a.getKeyStroke()); return mi; } /** * See return description * @return JMenuItem the "load view data from log file" - menu entry */ private JMenuItem createChangeLogfileMenu() { String command = COMMAND_CHANGE_LOGFILE; JMenuItem mi = new JMenuItem(command); mi.setToolTipText("load view data 'offline' from logfile"); SimpleAction a = new SimpleAction(command) { public int getMnemonic() { return KeyEvent.VK_L; } /** ctrl + L */ public KeyStroke getKeyStroke() { return KeyStroke.getKeyStroke(KeyEvent.VK_L, Event.CTRL_MASK); } public void actionPerformed(ActionEvent e) { String[] exts = new String[] { "txt", "log" }; String[] desc = new String[] { "textfiles", "logfiles" }; int mode = JFileChooser.OPEN_DIALOG; File[] ff = Toolz.getFilesFromChooser( System.getProperty("user.home"), mode, exts, desc, false, JFileChooser.FILES_ONLY, app); if (ff.length <= 0) { return; } File f = ff[0]; if (f == null) { return; } else { if (!f.getName().endsWith(".log") && !f.getName().endsWith(".txt")) { f = new File(f.getParentFile(), f.getName() + ".log"); } } app.setOffline(true); Connector c = app.getConnector(); c.setLogFile(f); if (app.isOffline()) { try { app.setPath(f.getAbsolutePath()); layout.setVersions(c.getData()); refreshLayout(true); } catch (Exception ee) { app.error("Cannot read offline data:\n" + ee.getMessage(), ee); } } } }; mi.addActionListener(a); mi.setAccelerator(a.getKeyStroke()); a.addPropertyChangeListener(new ActionChangedListener(mi)); actions.put(command, a); return mi; } /** * See return description * @return JMenuItem the "diff" - menu entry */ private JMenuItem createDiffMenu() { String command = COMMAND_DIFF; JMenuItem mi = new JMenuItem(command); mi.setToolTipText("please click another version to see difference"); SimpleAction a = new SimpleAction(command) { public void actionPerformed(ActionEvent e) { if (clicked != null) { diffView = clicked; diffView.setSelected(true); JOptionPane.showMessageDialog( app, "Please click another version to see difference", "Information:", JOptionPane.INFORMATION_MESSAGE); need_re_paint = true; repaint(); } } }; mi.addActionListener(a); a.addPropertyChangeListener(new ActionChangedListener(mi)); actions.put(command, a); return mi; } /** * See return description * @return JMenuItem the "open new window" - menu entry */ private JMenuItem createNewWindowMenu() { String command = COMMAND_NEW_WINDOW; JMenuItem mi = new JMenuItem(command); mi.setToolTipText("open new window with same view"); SimpleAction a = new SimpleAction(command) { /** ctrl + N */ public KeyStroke getKeyStroke() { return KeyStroke.getKeyStroke(KeyEvent.VK_N, Event.CTRL_MASK); } public void actionPerformed(ActionEvent e) { final String path = app.getPath(); try { app.getPropsSetter().storeProperties(); } catch (IOException ex) { app.error("Cannot store properties:\n" + ex.getMessage(), ex); } Thread t = new Thread(new Runnable() { public void run() { App app2 = new App(); app2.initFrame(new String[] { path, "-props" }); } }); t.start(); } }; mi.addActionListener(a); mi.setAccelerator(a.getKeyStroke()); a.addPropertyChangeListener(new ActionChangedListener(mi)); actions.put(command, a); return mi; } /** * See return description * @return JMenuItem the "load P4 info for all branches" - menu entry */ private JMenuItem createExploreAllMenu() { String command = COMMAND_EXPLORE_ALL_BRANCHES; JMenuItem mi = new JMenuItem(command); mi.setToolTipText("load P4 info for all branches (if available)"); SimpleAction a = new SimpleAction(command) { /** ctrl + I */ public KeyStroke getKeyStroke() { return KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.CTRL_MASK); } public void actionPerformed(ActionEvent e) { exploreAllVersions(); } }; mi.addActionListener(a); mi.setAccelerator(a.getKeyStroke()); a.addPropertyChangeListener(new ActionChangedListener(mi)); actions.put(command, a); return mi; } /** * See return description * @return JMenuItem the "load P4 info for selected branch" - menu entry */ private JMenuItem createExploreOneMenu() { String command = COMMAND_EXPLORE_ONE_BRANCH; JMenuItem mi = new JMenuItem(command); mi.setToolTipText("load P4 info for selected branch (if available)"); SimpleAction a = new SimpleAction(command) { public void actionPerformed(ActionEvent e) { if (clicked != null) { Version v = clicked.getVersion(); exploreVersion(v); } } }; mi.addActionListener(a); a.addPropertyChangeListener(new ActionChangedListener(mi)); actions.put(command, a); return mi; } /** * See return description * @return JMenuItem the "collapse / expand one" - menu entry */ private JMenuItem createCollapseOneMenu() { String command = COMMAND_COLLAPSE_BRANCH; JMenuItem mi = new JMenuItem(command); mi.setToolTipText("collapse / expand 'uninteresting' revisions on this branch"); SimpleAction a = new SimpleAction(command) { public void actionPerformed(ActionEvent e) { if (clicked != null) { layout.toggleCompact(clicked); Action a = (Action) actions.get(COMMAND_REDO_LAYOUT); a.actionPerformed(new ActionEvent(this, -1, COMMAND_REDO_LAYOUT)); } } }; mi.addActionListener(a); a.addPropertyChangeListener(new ActionChangedListener(mi)); actions.put(command, a); return mi; } /** * See return description * @return JMenuItem the "collapse / expand all" - menu entry */ private JMenuItem createCollapseAllMenu() { String command = COMMAND_COLLAPSE_ALL; JMenuItem mi = new JMenuItem(command); mi.setToolTipText("collapse / expand all 'uninteresting' revisions"); SimpleAction a = new SimpleAction(command) { /** ctrl + e */ public KeyStroke getKeyStroke() { return KeyStroke.getKeyStroke(KeyEvent.VK_E, Event.CTRL_MASK); } public void actionPerformed(ActionEvent e) { layout.toggleCompact(); Action a = (Action) actions.get(COMMAND_REDO_LAYOUT); a.actionPerformed(new ActionEvent(this, -1, COMMAND_REDO_LAYOUT)); } }; mi.addActionListener(a); mi.setAccelerator(a.getKeyStroke()); a.addPropertyChangeListener(new ActionChangedListener(mi)); actions.put(command, a); return mi; } /** * See return description * @return JMenuItem the "save available P4 data" - menu entry */ private JMenuItem createSaveAsTextMenu() { String command = COMMAND_SAVE_AS_TEXT; JMenuItem mi = new JMenuItem(command); mi.setToolTipText("save available P4 data, i.e. for 'offline' view"); SimpleAction a = new SimpleAction(command) { public int getMnemonic() { return KeyEvent.VK_T; } /** ctrl + t */ public KeyStroke getKeyStroke() { return KeyStroke.getKeyStroke(KeyEvent.VK_T, Event.CTRL_MASK); } public void actionPerformed(ActionEvent e) { String[] exts = new String[] { "txt", "log" }; String[] desc = new String[] { "textfiles", "logfiles" }; int mode = JFileChooser.SAVE_DIALOG; File[] ff = Toolz.getFilesFromChooser( System.getProperty("user.home"), mode, exts, desc, false, JFileChooser.FILES_ONLY, app); if (ff.length <= 0) { return; } File f = ff[0]; if (f == null) { return; } else { if (!f.getName().endsWith(".log") && !f.getName().endsWith(".txt")) { f = new File(f.getParentFile(), f.getName() + ".log"); } } try { Toolz.write(app.getConnector().dataToString(), f); } catch (Exception ee) { app.error("Cannot write data to file:\n" + ee.getMessage(), ee); } } }; mi.addActionListener(a); mi.setAccelerator(a.getKeyStroke()); a.addPropertyChangeListener(new ActionChangedListener(mi)); actions.put(command, a); return mi; } /** * See return description * @return JMenuItem the "copy P4 depot path" - menu entry */ private JMenuItem createCopyMenu() { String command = COMMAND_COPY; JMenuItem mi = new JMenuItem(command); mi.setToolTipText("copy P4 depot path to clipboard"); SimpleAction a = new SimpleAction(command) { public int getMnemonic() { return KeyEvent.VK_C; } /** ctrl + c */ public KeyStroke getKeyStroke() { return KeyStroke.getKeyStroke(KeyEvent.VK_C, Event.CTRL_MASK); } public void actionPerformed(ActionEvent e) { Version v = null; if(clicked != null){ v = clicked.getVersion(); } else if(diffView != null){ v = diffView.getVersion(); } if(v != null){ // this is a bug with clipboard in JDK < 1.4: // content of clipboard is not always really setted content, // but some times content of neighbor memory segment... Toolz.setClipboardContent("" + v.getPath()); } } }; mi.addActionListener(a); mi.setAccelerator(a.getKeyStroke()); a.addPropertyChangeListener(new ActionChangedListener(mi)); actions.put(command, a); return mi; } /** * See return description * @return JMenuItem the "copy available P4 data" - menu entry */ private JMenuItem createCopyDataMenu() { String command = COMMAND_COPY_DATA; JMenuItem mi = new JMenuItem(command); mi.setToolTipText("copy available P4 data as text to clipboard"); SimpleAction a = new SimpleAction(command) { public int getMnemonic() { return KeyEvent.VK_D; } /** ctrl + d */ public KeyStroke getKeyStroke() { return KeyStroke.getKeyStroke(KeyEvent.VK_D, Event.CTRL_MASK); } public void actionPerformed(ActionEvent e) { Version v = null; if(clicked != null){ v = clicked.getVersion(); } else if(diffView != null){ v = diffView.getVersion(); } if(v != null){ Toolz.setClipboardContent(v.toString()); } else { Toolz.setClipboardContent(app.getConnector().dataToString()); } } }; mi.addActionListener(a); mi.setAccelerator(a.getKeyStroke()); a.addPropertyChangeListener(new ActionChangedListener(mi)); actions.put(command, a); return mi; } /** * See return description * @return JMenuItem the "reset all custom user changes" - menu entry */ private JMenuItem createRedoLayoutMenu() { String command = COMMAND_REDO_LAYOUT; JMenuItem mi = new JMenuItem(command); mi.setToolTipText("reset all custom user changes to layout"); SimpleAction a = new SimpleAction(command) { public int getMnemonic() { return KeyEvent.VK_R; } /** f1 */ public KeyStroke getKeyStroke() { return KeyStroke.getKeyStroke(KeyEvent.VK_R, Event.CTRL_MASK); } public void actionPerformed(ActionEvent e) { layout.resetUserMoves(); refreshLayout(true); } }; mi.addActionListener(a); mi.setAccelerator(a.getKeyStroke()); a.addPropertyChangeListener(new ActionChangedListener(mi)); actions.put(command, a); return mi; } /** * See return description * @return JMenuItem the "create one HTML overview file" - menu entry */ private JMenuItem createSaveAsImageMenu() { JMenuItem mi; String command = COMMAND_SAVE_AS_IMAGE; mi = new JMenuItem(command); mi.setToolTipText("create one HTML overview file with generated GIF images"); SimpleAction a = new SimpleAction(command) { public int getMnemonic() { return KeyEvent.VK_S; } /** ctrl + s */ public KeyStroke getKeyStroke() { return KeyStroke.getKeyStroke(KeyEvent.VK_S, Event.CTRL_MASK); } public void actionPerformed(ActionEvent e) { String[] exts = new String[] { "html" }; //"jpg", String[] desc = new String[] { "html-files" }; //"jpeg-images", File[] ff = Toolz.getFilesFromChooser( System.getProperty("user.home"), JFileChooser.SAVE_DIALOG, exts, desc, false, JFileChooser.FILES_ONLY, app); if (ff.length <= 0) { return; } final File f = ff[0]; if (f == null) { return; } else { Thread t = new Thread(new Runnable() { public void run() { saveViewAsImage(f); } }); t.start(); // workaround for repaint bug repaint(); System.runFinalization(); System.gc(); } } }; mi.addActionListener(a); mi.setAccelerator(a.getKeyStroke()); a.addPropertyChangeListener(new ActionChangedListener(mi)); actions.put(command, a); return mi; } /** * See return description * @return JMenuItem the "view/set all available properties" - menu entry */ private JMenuItem createSetPropsMenu() { String command = COMMAND_SET_PROPS; JMenuItem mi = new JMenuItem(command); mi.setToolTipText("view/set all available properties"); SimpleAction a = new SimpleAction(command) { public int getMnemonic() { return KeyEvent.VK_P; } /** ctrl + p */ public KeyStroke getKeyStroke() { return KeyStroke.getKeyStroke(KeyEvent.VK_P, Event.CTRL_MASK); } public void actionPerformed(ActionEvent e) { if (props_pnl == null) { props_pnl = new PropertiesPanel(); } props_pnl.reset(); if (app.object(props_pnl, "Settings") != null) { app.setPropsEnabled(true); props_pnl.setProps(); } } }; mi.addActionListener(a); mi.setAccelerator(a.getKeyStroke()); a.addPropertyChangeListener(new ActionChangedListener(mi)); actions.put(command, a); return mi; } /** * See return description * @return JMenuItem the "view/set all available properties" - menu entry * @return JPanel der "x - space" Schieber */ private JPanel createXSpaceMenu() { JLabel lab; JPanel pan; lab = new JLabel("x space "); lab.setForeground(Color.black); pan = new JPanel(); final JSlider s2 = new JSlider(JSlider.HORIZONTAL, -5, 5, 0); s2.setToolTipText("increase/decrease space between branches"); s2.setMinorTickSpacing(1); s2.setMajorTickSpacing(5); s2.setPaintTicks(true); s2.setSnapToTicks(true); s2.setPreferredSize(new Dimension(200, 40)); s2.setSize(new Dimension(200, 40)); s2.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { if (!s2.getValueIsAdjusting() || !Toolz.isVmOk()) { Const.H_SPACE = Const.H_SPACE_D + s2.getValue() * Const.STEP_H; refreshLayout(false); } } }); pan.setLayout(new BorderLayout(2, 2)); pan.add(lab, BorderLayout.EAST); pan.add(s2, BorderLayout.CENTER); return pan; } /** * Method createXSpaceMenu erstellt den "y - space" Schieber * @return JPanel der "y - space" Schieber */ private JPanel createYSpaceMenu() { JLabel lab; JPanel pan; lab = new JLabel("y space "); lab.setForeground(Color.black); pan = new JPanel(); final JSlider s3 = new JSlider(JSlider.HORIZONTAL, -5, 5, 0); s3.setToolTipText("increase/decrease space between revisions"); s3.setMinorTickSpacing(1); s3.setMajorTickSpacing(5); s3.setPaintTicks(true); s3.setSnapToTicks(true); s3.setPreferredSize(new Dimension(200, 40)); s3.setSize(new Dimension(200, 40)); s3.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { if (!s3.getValueIsAdjusting() || !Toolz.isVmOk()) { Const.V_SPACE = Math.abs(Const.V_SPACE_D + s3.getValue() * Const.STEP_V); refreshLayout(false); } } }); pan.setLayout(new BorderLayout(2, 2)); pan.add(lab, BorderLayout.EAST); pan.add(s3, BorderLayout.CENTER); return pan; } /** * Method createZoomMenu erstellt den "zoom" Schieber * @return JPanel der "zoom" Schieber */ private JPanel createZoomMenu() { JLabel lab = new JLabel(" zoom "); lab.setForeground(Color.black); JPanel pan = new JPanel(); final JSlider s = new JSlider(JSlider.HORIZONTAL, -5, 5, 0); s.setToolTipText("increase/decrease view zoom"); s.setMinorTickSpacing(1); s.setMajorTickSpacing(5); s.setPaintTicks(true); s.setSnapToTicks(true); s.setPreferredSize(new Dimension(200, 40)); s.setSize(new Dimension(200, 40)); s.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { if (!s.getValueIsAdjusting() || !Toolz.isVmOk()) { double dx = 1 + (double) (s.getValue() * 0.15); zoom(dx,dx); } } }); pan.setLayout(new BorderLayout(2, 2)); pan.add(lab, BorderLayout.EAST); pan.add(s, BorderLayout.CENTER); zoom_slider = s; return pan; } /** * Constructor for MenuCreator. */ public MenuCreator() { super(); } } /** * Perform save operation. Entire view will be saved on one or more gif images * with one html - index file. Images will be successive numbered. * @param f "base" file name to calculate real file names for multiple gif/html files */ protected void saveViewAsImage(File f) { Dimension d = getPreferredSize(); String message = "Images and HTML overview are successfully saved!"; BufferedOutputStream out = null; FileOutputStream fo = null; GifEncoder encoder = null; int image_width = 600; int image_height = 400; int count_width = d.width / image_width; int count_height = d.height / image_height; if(count_width <= 1){ image_width = d.width; } if(count_height <= 1){ image_height = d.height; } if(d.width - count_width * image_width > 0){ count_width ++; } if(d.height - count_height * image_height > 0){ count_height ++; } String prefix = f.getName(); String suffix = ".html"; String suffix_img = ".gif"; String cancel_txt = "Cancel"; if (prefix.endsWith(suffix)) { //f = new File(f.getParentFile(), f.getName() + ".gif"); prefix = prefix.substring(0, prefix.length() - suffix.length()); } BufferedImage img = new BufferedImage(image_width, image_height, BufferedImage.TYPE_INT_RGB); Graphics2D gr = (Graphics2D) img.getGraphics(); gr.setBackground(Color.white); gr.clearRect(0,0, image_width, image_height); //gr.scale(scale_x_axis, scale_y_axis); int dx = 0; int dy = 0; int y_position = 0; int x_position = 0; int images_nbr = count_height * count_width; final JProgressBar progressBar = new JProgressBar(JProgressBar.HORIZONTAL, 0, images_nbr); final JLabel label = new JLabel(); Thread messageThread = null; final JPanel panel = new JPanel(new BorderLayout(3,3)); final JOptionPane pane = new JOptionPane( panel, JOptionPane.INFORMATION_MESSAGE, JOptionPane.DEFAULT_OPTION, null, new String[]{cancel_txt} ); final JDialog dialog = pane.createDialog(app, "Information"); if(images_nbr > 1){ Runnable run = new Runnable() { public void run() { dialog.setModal(true); panel.add(progressBar, BorderLayout.CENTER); panel.add(label, BorderLayout.SOUTH); dialog.show(); dialog.setModal(false); } }; messageThread = new Thread(run); //messageThread.setPriority(Thread.MAX_PRIORITY); messageThread.start(); } StringBuffer sb = new StringBuffer(); sb.append("<html>\n<head>\n<title>").append(app.getPath()).append("</title>\n</head>\n<body>"); sb.append("\n<table cellpadding='0' cellspacing='0' border='0'>"); String fileName = ""; int count = 0; for (int i = 0; i < count_height; i++) { y_position += dy; dx = 0; x_position = 0; if(dy == 0){ dy = -image_height; } sb.append("\n<tr>"); for (int j = 0; j < count_width; j++) { Object selectedValue = pane.getValue(); if(cancel_txt.equals(selectedValue) || !dialog.isModal()){ break; } try { gr.translate(x_position, y_position); if (layout != null) { layout.paint(gr); } fileName = prefix + "_" + i + "_" + j + suffix_img; sb.append("\n\t<td><img src='").append(fileName).append("' width='"); sb.append(image_width).append("' height='").append(image_height).append("'></td>"); f = new File(f.getParentFile(), fileName); fo = new FileOutputStream(f); out = new BufferedOutputStream(fo); encoder = new GifEncoder(img, out, false); encoder.setColorModel(ColorModel.getRGBdefault()); encoder.encode(); gr.clearRect(-x_position, -y_position, image_width, image_height); //gr.clearRect(-x_position, -y_position, (int)(image_width/scale_x_axis), (int)(image_height/scale_y_axis)); //gr.clearRect(-x_position, -y_position, (int)((-x_position - image_width)/scale_x_axis), (int)((-y_position - image_height)/scale_y_axis)); gr.translate(-x_position, -y_position); if(dx == 0){ dx = -image_width; } x_position += dx; out.flush(); out.close(); fo = null; out = null; progressBar.setValue(++count); label.setText("Generate GIF files, " + ((count*100)/images_nbr) + "% ready ..."); } catch (Throwable t) { message = Toolz.getStackTrace(t); if (fo != null) { try { fo.close(); } catch (IOException io) { message += "\n\n" + Toolz.getStackTrace(io); } } } } sb.append("\n</tr>"); } sb.append("\n</table>\n</body>\n</html>"); label.setText(message); try { f = new File(f.getParentFile(), prefix + ".html"); fo = new FileOutputStream(f); out = new BufferedOutputStream(fo); out.write(sb.toString().getBytes()); out.flush(); out.close(); dialog.setVisible(false); dialog.dispose(); } catch (Exception e) { message = Toolz.getStackTrace(e); if (fo != null) { try { fo.close(); } catch (IOException io) { message += "\n\n" + Toolz.getStackTrace(io); } } } if(messageThread!=null){ messageThread = null; } } /** Overriden * @return one rectangle, that has always 0,0 x,y coordinates and * the width and height of the view * @see java.awt.Component#getBounds(Rectangle) */ public Rectangle getBounds(Rectangle rv) { if(rv == null){ rv = new Rectangle(); } // 0,0: this is a hack to handle wrong size of scrollpane !! rv.setBounds( 0, 0, getWidth(), getHeight() ); return rv; } /** Overriden * @return one rectangle, that has always 0,0 x,y coordinates and * the width and height of the view * @see java.awt.Component#getBounds() */ public Rectangle getBounds() { return getBounds(new Rectangle()); } /** Overriden * @return view dimension * @see java.awt.Component#getMaximumSize() */ public Dimension getMaximumSize() { return getPreferredSize(); } /** Overriden * @return view dimension * @see java.awt.Component#getMinimumSize() */ public Dimension getMinimumSize() { return getPreferredSize(); } /** Overriden * @return always false * @see java.awt.Component#isDoubleBuffered() */ public boolean isDoubleBuffered() { return false; } /** Overriden * @return always true * @see javax.swing.JComponent#isMaximumSizeSet() */ public boolean isMaximumSizeSet() { return true; } /** Overriden * @return always true * @see javax.swing.JComponent#isMinimumSizeSet() */ public boolean isMinimumSizeSet() { return true; } /** Overriden * @return always true * @see javax.swing.JComponent#isPreferredSizeSet() */ public boolean isPreferredSizeSet() { return true; } /** Overriden * @return always true * @see javax.swing.JComponent#isRequestFocusEnabled() */ public boolean isRequestFocusEnabled() { return true; } } /** * Listener for actions, for autoupdate of menu entries state */ class ActionChangedListener implements PropertyChangeListener { /** Button or menu, that will be updated, if action state is changed */ AbstractButton menuItem; /** * @param b Button or menu, that will be updated, if action state is changed */ ActionChangedListener(AbstractButton b) { super(); this.menuItem = b; } /** Will be called if coressponding action changed their state. * Update ctate of connected button * @param e the event */ public void propertyChange(PropertyChangeEvent e) { String propertyName = e.getPropertyName(); if (propertyName.equals("enabled")) { Boolean enabledState = (Boolean) e.getNewValue(); menuItem.setEnabled(enabledState.booleanValue()); } } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#5 | 2735 | andrei_loskutov | sources are now included to binary distribution at http://andrei.gmxhome.de/perforce/index.html | ||
#4 | 2554 | andrei_loskutov |
#25.12.2002 23:29 Revision 1.3.0.4 beta 1) Some small refactoring 2) First integration with P4eclipse: properties etc, thanks Boris Pr�ssmann 5) Better integration to Eclipse: use Eclipse internal compare tool for diff. 6) Fix some minor bugs. #14.12.2002 23:15 Revision 1.3.0.3 beta 1) Fix endless loop bug if p4 path was not set. 2) Fix never getting focus bug after error message shown 3) Fix painting bug on JDK 1.4.1 (must be verified!!!) 4) Some small refactoring 5) Fix error handling. 6) Fix some minor bugs. |
||
#3 | 2552 | andrei_loskutov | 1.3.0.4 changes | ||
#2 | 2460 | andrei_loskutov |
1) Change configuration: always reed properties 2) Change password store to hex format/password field 3) Create standalone/not standalone state for new branchview eclipse wrapper 4) Refactor all main classes to remove cyclic package dependency 5) Add workaround to fix new JDK 1.4.1 Swing bugs on JScrollPane/JViewPort 6) Add dispose methods to panel/connector 7) Minor internal GUI fixes/refactorings |
||
#1 | 2222 | andrei_loskutov |
#13.10.2002 22:16 Revision 1.2.1 1) Documentation task is finished... Yes!!! 2) New feature: copy P4 depot path 3) Some small fixes to menu shortkeys belongs ctrl + c and ctrl + d 4) Clipboard fix because of JDK 1.2 - 1.3 bug 5) Smallest refactoring on some methods. 6) Html help updated |