/******************************* 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.Dimension; import java.awt.Point; import java.awt.Toolkit; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Properties; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JViewport; import javax.swing.ListSelectionModel; import javax.swing.UIManager; import adiag.Const; import adiag.connection.Connector; import adiag.connection.FileLogConnector; import adiag.connection.P4CommandConnector; import adiag.util.Toolz; /** * The Applications-window with single view of P4 versions and capability to * maintain configuration file * @author Andrei Loskutov */ public class App extends JFrame { /** current connector */ private Connector conn; /** current view panel */ private ViewPanel currView; /** is debug on? */ private boolean debug; /** is props on ? */ private boolean propsEnable; /** is offline modus ? */ private boolean offline; /** should we fetch all branches by start ? * (try to discover all known branches) */ private boolean discover_all = true; /** error description, for start/init problems */ private String start_error; /** the path to config (branchview.ini) file */ private static String iniUrl; /** default view dimension */ private Dimension start_dimension = new Dimension(300, 300); /** the initial path to requested file(s) from Perforce - repository */ private String path; /** path to external diff tool */ private String diffPath = Const.DIFF_FILE; /** scroll pane with current view */ private JScrollPane scroll; /** Number of simultaneously opened windows */ private static int instanceNumber; /** administrator of settings and config - file */ protected static PropertySetter propsSetter; /** frame icon source as p4 logo image (orange/yellow) */ public static final byte[] LOGO = new byte[] { 71,73,70,56,57,97,16,0,16,0,-94,4,0,-1,-74,0,-1,-1,-1,0,0,0,-128,91,0,-52,-52,-52,0,0, 0,0,0,0,0,0,0,33,-7,4,1,0,0,4,0,44,0,0,0,0,16,0,16,0,0,3,67,72,-70,-36,-2,45,-56, 25,20,29,22,-24,45,72,-40,-104,-89,9,31,64,110,64,88,-98,102,-87,-87,-29,-22,-90, 89,59,-94,-76,-120,-105,-60,91,-89,-125,83,-25,85,113,117,74,18,-112,-50,-76,68,97, -116,4,-127,84,-6,58,14,-82,29,-58,-11,10,-23,122,29,9,0,59 }; /** frame icon as p4 logo image (orange/yellow) */ public static final ImageIcon ICON = new ImageIcon(LOGO); /** * Default-Constructor. */ public App() { super(); instanceNumber ++; if(propsSetter == null){ propsSetter = new PropertySetter(); } propsSetter.register(this); } /** * see return description * @return Currently used connector or null */ public Connector getConnector() { return conn; } /** * see return description * @return Current view instance */ public ViewPanel getCurrView() { return currView; } /** * see return description * @return String path to config file */ protected static String getIniUrl(){ if(iniUrl == null){ iniUrl = System.getProperty("user.home") + System.getProperty("file.separator") + Const.INI_FILE; } return iniUrl; } /** * see return description * @return PropertySetter administrator of settings and config - file */ public PropertySetter getPropsSetter(){ if(propsSetter == null){ propsSetter = new PropertySetter(); } return propsSetter; } /** * Starts the application. * @param args an array of command-line arguments, see online help */ public static void main(String[] args) { try { UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); } catch (Exception e) {} App app = new App(); app.initFrame(args); } /** * Init application * @param args command-line arguments, see online help */ void initFrame(String[] args) { setIconImage(ICON.getImage()); this.setState(JFrame.ICONIFIED); try { parseParameter(args); setCurrentView(new ViewPanel(getConnector().getData(), this)); scroll = new JScrollPane(getCurrView()); if(Toolz.isVmOk()){ /// this is feature of JDK >= 1.3 try{ scroll.getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE); } catch (Exception e){} } else { //scroll.getViewport().setBackingStoreEnabled(false); //scroll.getViewport().setOpaque(false); } } catch (Exception e) { // show error message in pane scroll = catchInitException(e); } this.setContentPane(scroll); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { Connector c = getConnector(); if (c != null) { try { if (isDebug() && !isOffline()) { try { Toolz.write(c.dataToString(), c.getLogFile()); } catch (Exception ee) { error("Cannot write log file:\n" + ee.getMessage(), ee); } } c.close(); } catch (IOException ioe) { error("Cannot close connector:\n" + ioe.getMessage(), ioe); } } instanceNumber --; if(instanceNumber <=0){ // last window if (isPropsEnabled()) { try { propsSetter.storeProperties(); } catch (IOException ee) { error("Cannot write properties:\n" + ee.getMessage(), ee); } } System.exit(0); } }; }); setBestAppSizeAndLocation(getCurrView().getPreferredSize()); this.setState(JFrame.NORMAL); pack(); try{ show(); setVisible(true); repaint(); } catch (Throwable e){ // sometimes happend.... e.printStackTrace(); scroll = catchInitException(e); this.setContentPane(scroll); setBestAppSizeAndLocation(start_dimension); show(); setVisible(true); repaint(); } this.getCurrView().requestFocus(); if(start_error!= null){ this.error(start_error, null); start_error = null; } } /** * set best (calculated ;) ) size and location of window * @param viewSize the size of view */ public void setBestAppSizeAndLocation(Dimension viewSize) { Dimension screen_dim = Toolkit.getDefaultToolkit().getScreenSize(); Dimension current_dim = getSize(); // small space from left/top of screen int margin_left = 60; int margin_top = 60; double width = screen_dim.getWidth(); double height = screen_dim.getHeight(); double v_w = viewSize.getWidth(); double v_h = viewSize.getHeight(); height -= margin_top * 2; width -= margin_left * 2; // not to small size! if (v_h > margin_top && v_h < height) { height = v_h + margin_top/2; } else if (v_h < margin_top) { height = margin_top; } if (v_w < width && v_w > margin_left) { width = v_w + margin_left/2; } else if( v_w < margin_left) { width = margin_left; } if (Math.abs(current_dim.getHeight() - height) > margin_top || Math.abs(current_dim.getWidth() - width) > margin_left) { Dimension dim = new Dimension((int) width, (int) height); if (scroll != null) { //scroll.setSize(dim); scroll.setPreferredSize(dim); //scroll.getViewport().setViewSize(dim); } setSize( new Dimension((int) width + 20, (int) height + 20)); pack(); Point newLocation = new Point(margin_left, margin_top); Point p = this.getLocation(); if (!isVisible() || p.getX() + width > screen_dim.getWidth() || p.getY() + height > screen_dim.getHeight()) { setLocation(newLocation); } } } /** * Show start errors * @param e error * @return JScrollPane with error description */ private JScrollPane catchInitException(Throwable e) { JScrollPane scroll; StringWriter sout = new StringWriter(1000); PrintWriter out = new PrintWriter(sout); e.printStackTrace(out); Connector c = getConnector(); try { if (c != null) { c.close(); } sout.close(); out.close(); } catch (IOException ioe) { ioe.printStackTrace(out); } out.flush(); ViewPanel ta = new ViewPanel(); setTitle("Error!"); scroll = new JScrollPane(ta); setCurrentView(ta); System.out.println("Error by initialisation:\n" + sout.toString()); return scroll; } /** * Set current connector * @param newConn new connector */ public void setConnector(Connector newConn) { Connector c = getConnector(); // close connector and write log if available if(c != null) { try { if (isDebug() && !isOffline()) { Toolz.write(c.dataToString(), c.getLogFile()); } c.close(); } catch (Exception ee) { error("Cannot write log file:\n" + ee.getMessage(), ee); } } this.conn = newConn; } /** * Set current view * @param newCurrView new view */ private void setCurrentView(ViewPanel newCurrView) { currView = newCurrView; } /** * See return description * @return boolean true, if we must write log file */ public boolean isDebug() { return this.debug; } /** * Set flag to write log file * @param on true, if we must write log file */ public void setDebug(boolean on) { this.debug = on; } /** * Set flag to use config file * @param on true, if we get all initial parameter from config file */ public void setPropsEnabled(boolean on) { this.propsEnable = on; } /** * See return description * @return boolean true, if we use config file instead of command line parameters */ public boolean isPropsEnabled() { return this.propsEnable; } /** * Set on/offline mode * @param on true to receive data from "old" log file or "offline" */ public void setOffline(boolean on) { Connector c = getConnector(); if(this.offline == on && c != null){ return; } this.offline = on; if (isOffline()) { c = new FileLogConnector(this); System.out.println("working offline..."); } else { c = new P4CommandConnector(this); System.out.println("working online..."); } setConnector(c); } /** * See return description * @return boolean true, if we are in offline mode (no direct connection to p4 process) */ public boolean isOffline() { return this.offline; } /** * Set flag to discover all known branches at one time * @param on true for all, false only for selected */ public void setDiscoverAllBranches(boolean on){ this.discover_all = on; } /** * See return description * @return boolean true to discover all known branches at one time, * false only for selected branch */ public boolean isDiscoverAllBranches(){ return this.discover_all; } /** * Prepare environement. This is not really clear and conventional, but fast ;) * @param param die Parameter: see online help :) * @throws Exception if something goes wrong */ private void parseParameter(String[] param) throws Exception { String [] oldParam = param; if(Toolz.indexOfString(param, "-props") >= 0){ if(!propsSetter.isInitDone()){ propsSetter.readProps(); } setDebug(propsSetter.getProperty(Const.PROPERTY_DEBUG).equalsIgnoreCase("true")); setOffline(propsSetter.getProperty(Const.PROPERTY_OFFLINE).equalsIgnoreCase("true")); setDiscoverAllBranches(propsSetter.getProperty(Const.PROPERTY_DISCOVER_ALL).equalsIgnoreCase("true")); setPropsEnabled(true); } else { // prepare environement: on Windows-systems is all in registry propsSetter.setProperty(Const.PROPERTY_DEBUG, "" + (Toolz.indexOfString(param, "-stat") >= 0)); propsSetter.setProperty(Const.PROPERTY_OFFLINE, "" + (Toolz.indexOfString(param, "-offline") >= 0)); propsSetter.setProperty(Const.PROPERTY_DISCOVER_ALL, "" + (Toolz.indexOfString(param, "-all") >= 0)); } // cleanup param = Toolz.remove(param, "-props"); param = Toolz.remove(param, "-stat"); param = Toolz.remove(param, "-offline"); param = Toolz.remove(param, "-all"); if (param.length == 1 ) { setPath(param[0]); } else if (param.length > 3) { propsSetter.setProperty(Const.PROPERTY_P4_USER, param[0]); propsSetter.setProperty(Const.PROPERTY_P4_CLIENT, param[1]); propsSetter.setProperty(Const.PROPERTY_P4_PORT, param[2]); setPath(param[3]); if(param.length > 4){ propsSetter.setProperty(Const.PROPERTY_P4_EXEC, param[4]); if(param.length > 5 ){ propsSetter.setProperty(Const.PROPERTY_P4_PASSWD, param[5]); } } } else { propsSetter.setProperty(Const.PROPERTY_OFFLINE, "true"); start_error = "Error: wrong parameter (or nothing) received!\n\nReceived parameter are:\n" + Toolz.makeLine(oldParam, "\n") + "--------------\nDon't worry, press F1 for help!"; } } /** * See return description * @param html true for HTML format, false for plain text * @return String The "online" - help to application command line parameters */ public static String getParameterHelp(boolean html){ String lineBreak = "\n"; if(html){ lineBreak = "<p>"; } StringBuffer sb = new StringBuffer(500); sb.append("<b>Possible parameters</b> (order is important!):").append(lineBreak); sb.append("1) P4 user name").append(lineBreak); sb.append("2) P4 client name").append(lineBreak); sb.append("3) P4 server port like 'server_name.domain_name:port'").append(lineBreak); sb.append("4) P4 file path to depot file like '//a/b/c.txt'").append(lineBreak); sb.append("5) Optional: P4 executable path like 'c:\\progra~2\\perforce'").append(lineBreak); sb.append("6) Optional: user password").append(lineBreak); sb.append("7) Optional: '-props', to use property file and not parameter").append(lineBreak); sb.append("8) Optional: '-stat', to write log with data to file tp4branchview.log in user directory").append(lineBreak); sb.append("9) Optional: '-offline', to read input data from older BranchView logfile").append(lineBreak); sb.append("10) Optional: '-all', to fetch all known 'branches' from p4").append(lineBreak); sb.append("If '-props' flag is set, awaiting only P4 file path to depot file as first parameter").append(lineBreak); sb.append("like '//a/b/c.txt', all other parameters will be read from").append(lineBreak); sb.append("property file 'tp4branchview.ini', that must be in user home directory").append(lineBreak); sb.append("NT: %SystemRoot%\\profiles\\%UserName%,").append(lineBreak); sb.append("Windows 2000 %SystemDrive%\\\\my documents\\%UserName%").append(lineBreak); sb.append("<b>Simple property file</b> (will be used only if '-props' flag is passed):").append(lineBreak); sb.append("p4.user=Andy_the_Great").append(lineBreak); sb.append("p4.client=andy").append(lineBreak); sb.append("p4.port=1666").append(lineBreak); sb.append("p4.executable=p4.exe").append(lineBreak); sb.append("p4.password=xxxxxx").append(lineBreak); sb.append("p4.save_log=true").append(lineBreak); sb.append("p4.read_log_offline=false").append(lineBreak); sb.append("p4.fetch_all_branches=true").append(lineBreak); sb.append("p4.diff_exe_path=examdiff.exe").append(lineBreak); return sb.toString(); } /** * See return description * @param html true for HTML format, false for plain text * @return String The "online" - help to application (GUI) usage */ public static String getGuiHelp(boolean html){ String lineBreak = "\n"; if(html){ lineBreak = "<p>"; } StringBuffer sb = new StringBuffer(500); sb.append("<b>Keyboard usage:</b>").append(lineBreak); sb.append("CTRL + E collapse/expand either selected or all branches").append(lineBreak); sb.append("CTRL + C copy P4 depot path of selected revision to clipboard").append(lineBreak); sb.append("CTRL + D copy P4 data of the selected revision to clipboard").append(lineBreak); sb.append("CTRL + D copy P4 data over all known revisions to clipboard, if nothing selected").append(lineBreak); sb.append("CTRL + L open dialog to load data for view from log file").append(lineBreak); sb.append("CTRL + N open new window with same content").append(lineBreak); sb.append("CTRL + P open properties window").append(lineBreak); sb.append("CTRL + R redo view layout").append(lineBreak); sb.append("CTRL + S open dialog to save view as GIF-image(s) with one HTML overview file").append(lineBreak); sb.append("CTRL + T open dialog to save all view data as text").append(lineBreak); sb.append("CTRL + V change view to path from clipboard, if clipboard contains valid P4 depot path").append(lineBreak); sb.append("CTRL + '+' increase zoom (work only for current view)").append(lineBreak); sb.append("CTRL + '-' decrease zoom (work only for current view)").append(lineBreak); sb.append("'Pos 1' jump up").append(lineBreak); sb.append("'End' jump down").append(lineBreak); sb.append("F1 show online help").append(lineBreak); sb.append("<b>Mouse usage:</b>").append(lineBreak); sb.append("2x left click over revision fetch more data (if available) from Perforce.").append(lineBreak); sb.append("1x left click select first revision for a diff-tool,").append(lineBreak); sb.append("(deleted revisions cannot be compare!)").append(lineBreak); sb.append("everyone other clicked revision will be a second revision for a diff-tool.").append(lineBreak); sb.append("1x click over empty background skip current selection.").append(lineBreak); sb.append("1x left click <b>and</b> drag over revision move this revision,").append(lineBreak); sb.append("1x left click <b>and</b> drag over background scroll the view").append(lineBreak); sb.append("1x left click <b>+ CTRL + SHIFT hold</b> and drag over a revision move entire view.").append(lineBreak); sb.append("1x right click, (or 1x left click + CTRL hold) and drag over a revision move entire branch.").append(lineBreak); sb.append("1x right click over background open a general menu.").append(lineBreak); sb.append("1x right click over a revision open a special revision menu.").append(lineBreak); return sb.toString(); } /** * Set current path to retrieve data from P4 * @param newpath the current path to P4 file */ public void setPath(String newpath) { this.path = newpath; if(isOffline()){ this.setTitle("Offline: " + getConnector().getLogFile().getAbsolutePath()); } else { this.setTitle(path); } } /** * See return description * @return the current path to P4 file */ public String getPath() { return path; } /** * Set new path to diff tool * @param newpath path to diff tool */ public void setDiffPath(String newpath) { this.diffPath = newpath; } /** * See return description * @return current path to diff tool */ public String getDiffPath() { return diffPath; } /** * Show custom panel + buttons "ok" and "cancel" as modal dialog * @param panel custom panel to edit by user * @param title dialog title * @return given panel (data may be changed by user), or null if user has "cancel" dialog */ public Object object(Object panel, String title) { Object[] options = { "Ok", "Cancel" }; int i = JOptionPane.showOptionDialog( this, panel, title, JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]); if (i == JOptionPane.YES_OPTION) { return panel; } else { return null; } } /** * Show custom panel + buttons "ok" and "cancel" as modal dialog * @param panel custom panel to edit by user * @param title dialog title * @return given panel (data may be changed by user), or null if user has "cancel" dialog */ public Object [] select(Object[] elements, String title) { Arrays.sort(elements); JList list = new JList(elements); list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); JScrollPane scroll = new JScrollPane(list); JPanel panel = new JPanel(new BorderLayout(3,3)); JLabel label = new JLabel(title); panel.add(label, BorderLayout.NORTH); panel.add(scroll, BorderLayout.CENTER); if(object(panel, "Please select files!") != null){ return list.getSelectedValues(); } else { return new Object[0]; } } /** * Show error dialog * @param message error description for user * @param error error instance or null */ public void error(String message, Throwable error) { if(!this.isVisible()){ System.out.println(message); } else { JOptionPane.showMessageDialog(this, message, "Error!", JOptionPane.ERROR_MESSAGE); } if(error != null){ error.printStackTrace(); } } /** * Show yes/no dialog * @param message description for user * @return user choice, true for "yes", false elsewhere */ public boolean yesno(String message) { Object[] options = { "yes", "no" }; int a = JOptionPane.showOptionDialog( this, message, "Question", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, null); return a == JOptionPane.YES_OPTION; } /** * Administrator class for settings and config - file * @author Andy the Great */ public class PropertySetter { /** is props file read yet? */ private boolean init; /** current application properties */ private Properties props; /** Known windows instances */ private ArrayList apps = new ArrayList(); /** * See return description * @return Properties curent config object */ protected Properties getProps() { if (props == null) { try { readProps(); } catch (IOException e) { error("Cannot read properties file:\n" + e.getMessage(), e); } } return props; } /** * See return description * @return ArrayList with all props keys */ public ArrayList getKeyList(){ return new ArrayList(getProps().keySet()); } /** * Write props file to HD * @throws ExplainedException, if something goes wrong */ public void storeProperties() throws IOException { OutputStream out; File f = new File(getIniUrl()); out = new BufferedOutputStream(new FileOutputStream(f)); getProps().store(out, "t4BranchView tool configuration file, please don't edit if you don't understand!"); out.close(); } /** * See return description * @return boolean true, if props file are read */ public boolean isInitDone(){ return init; } /** * Read props from HD * @throws IOException if something goes wrong */ public void readProps() throws IOException { if (props == null) { props = new Properties(); } File f = new File(getIniUrl()); InputStream is; try { is = new BufferedInputStream(new FileInputStream(f)); props.load(is); is.close(); } catch (FileNotFoundException e) { try { f.createNewFile(); } catch (Exception ex) { String message = "The ini - file " + f.getPath() + " cannot be created.\n"; error(message + e.getMessage(), e); } } // set to default for (int i = 0; i < Const.PROPERTIES.length; i++) { if (!props.containsKey(Const.PROPERTIES[i])) { props.put(Const.PROPERTIES[i], getProperty(Const.PROPERTIES[i])); } } init = true; } /** * Constructor for PropertySetter. */ public PropertySetter() { super(); } /** * Store given window instance to "current windows" list * @param app window instance */ public void register(App app){ App a; for (int i = 0; i < apps.size(); i++) { a = (App)apps.get(i); if(a.equals(app)){ return; } } apps.add(app); } /** * Remove given window instance from "current windows" list * @param app window instance */ public void unregister(App app){ App a; for (int i = 0; i < apps.size(); i++) { a = (App)apps.get(i); if(a.equals(app)){ apps.remove(i); } } } /** * See return description * @param key option name, one of Const.PROPERTY_ - constants * @return String current value for given props key, or empty string */ public String getProperty(String key){ String value = getProps().getProperty(key, ""); if(value.trim().length() == 0){ if(Const.PROPERTY_DEBUG.equals(key)){ return "" + isDebug(); } else if(Const.PROPERTY_OFFLINE.equals(key)){ return "" + isOffline(); } else if(Const.PROPERTY_DISCOVER_ALL.equals(key)){ return "" + isDiscoverAllBranches(); } else if(Const.PROPERTY_DIFF_FILE.equals(key)){ return getDiffPath(); } } return value; } /** * Set current value for given props key, and update all known windows * instances * @param key option name, one of Const.PROPERTY_ - constants * @param value current value for given props key */ public void setProperty(String key, String value){ key = key == null ? "" : key.trim(); getProps().setProperty(key, value); App a; if(Const.PROPERTY_DEBUG.equals(key)){ for (int i = 0; i < apps.size(); i++) { a = (App)apps.get(i); a.setDebug(Boolean.valueOf(value).booleanValue()); } } else if(Const.PROPERTY_OFFLINE.equals(key)){ for (int i = 0; i < apps.size(); i++) { a = (App)apps.get(i); a.setOffline(Boolean.valueOf(value).booleanValue()); } } else if(Const.PROPERTY_DISCOVER_ALL.equals(key)){ for (int i = 0; i < apps.size(); i++) { a = (App)apps.get(i); a.setDiscoverAllBranches(Boolean.valueOf(value).booleanValue()); } } else if(Const.PROPERTY_DIFF_FILE.equals(key)){ for (int i = 0; i < apps.size(); i++) { a = (App)apps.get(i); a.setDiffPath(value); } } } } }
# | 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 |