/plugins/SVNPlugin/tags/0.9.2/src/ise/plugin/svn/gui/LogResultsPanel.java

# · Java · 388 lines · 267 code · 36 blank · 85 comment · 28 complexity · 9a84bd85dd9d44ff0d6760f570f6e6c5 MD5 · raw file

  1. /*
  2. Copyright (c) 2007, Dale Anson
  3. All rights reserved.
  4. Redistribution and use in source and binary forms, with or without modification,
  5. are permitted provided that the following conditions are met:
  6. * Redistributions of source code must retain the above copyright notice,
  7. this list of conditions and the following disclaimer.
  8. * Redistributions in binary form must reproduce the above copyright notice,
  9. this list of conditions and the following disclaimer in the documentation
  10. and/or other materials provided with the distribution.
  11. * Neither the name of the author nor the names of its contributors
  12. may be used to endorse or promote products derived from this software without
  13. specific prior written permission.
  14. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  15. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  16. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  17. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  18. ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  19. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  20. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  21. ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  22. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  23. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24. */
  25. package ise.plugin.svn.gui;
  26. import java.awt.Color;
  27. import java.awt.Component;
  28. import java.awt.Dimension;
  29. import java.awt.Point;
  30. import java.awt.event.*;
  31. import java.io.File;
  32. import java.io.Serializable;
  33. import java.text.SimpleDateFormat;
  34. import java.util.*;
  35. import javax.swing.*;
  36. import javax.swing.event.*;
  37. import javax.swing.table.*;
  38. import javax.swing.border.EmptyBorder;
  39. import ise.java.awt.LambdaLayout;
  40. import ise.plugin.svn.PVHelper;
  41. import ise.plugin.svn.action.CopyAction;
  42. import ise.plugin.svn.action.DiffAction;
  43. import ise.plugin.svn.data.CopyData;
  44. import ise.plugin.svn.data.SVNData;
  45. import ise.plugin.svn.data.LogResults;
  46. import ise.plugin.svn.library.GUIUtils;
  47. import ise.plugin.svn.library.TableCellViewer;
  48. import org.tmatesoft.svn.core.SVNLogEntry;
  49. import org.tmatesoft.svn.core.SVNLogEntryPath;
  50. import org.tmatesoft.svn.core.wc.SVNInfo;
  51. import org.tmatesoft.svn.core.wc.SVNRevision;
  52. import org.tmatesoft.svn.core.SVNURL;
  53. import org.gjt.sp.jedit.GUIUtilities;
  54. import org.gjt.sp.jedit.View;
  55. /**
  56. * Shows the results of an svn log command. The display shows 4 or optionally 5
  57. * columns, with the 5th column being the paths of other files committed in the
  58. * same revision.
  59. */
  60. public class LogResultsPanel extends JPanel {
  61. private View view = null;
  62. private LogResults logResults = null;
  63. private String username = null;
  64. private String password = null;
  65. /**
  66. * @param map with path/file name as key, a list of associated log entries as the value
  67. * @param showPaths whether or not path information for other files associated with each
  68. * revision are included in the log entries
  69. * @param view the parent frame
  70. * @param username not used here, but passed on to commands available in the context menu
  71. * @param password password for username
  72. */
  73. public LogResultsPanel( LogResults logResults, boolean showPaths, View view, String username, String password ) {
  74. super( new LambdaLayout() );
  75. this.view = view;
  76. this.logResults = logResults;
  77. this.username = username;
  78. this.password = password;
  79. setBorder( new EmptyBorder( 3, 3, 3, 3 ) );
  80. LambdaLayout.Constraints con = LambdaLayout.createConstraint();
  81. con.a = LambdaLayout.W;
  82. con.s = "w";
  83. con.p = 0;
  84. TreeMap < String, List < SVNLogEntry >> results = logResults.getEntries();
  85. Set < Map.Entry < String, List < SVNLogEntry >>> set = results.entrySet();
  86. for ( Map.Entry < String, List < SVNLogEntry >> me : set ) {
  87. String path = ( String ) me.getKey();
  88. JLabel label = new JLabel( "Path: " + path );
  89. // sort the entries
  90. List<SVNLogEntry> entries = me.getValue();
  91. Collections.sort( entries, new EntryComparator() );
  92. // put the results data into an array to pass to a JTable. Columns
  93. // are revision, data, author, comment, and associated files.
  94. String[][] data = new String[ entries.size() ][ showPaths ? 5 : 4 ];
  95. Iterator it = entries.iterator();
  96. for ( int i = 0; it.hasNext(); i++ ) {
  97. SVNLogEntry entry = ( SVNLogEntry ) it.next();
  98. String revision = String.valueOf( entry.getRevision() );
  99. String date = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss Z", Locale.getDefault() ).format( entry.getDate() );
  100. String author = entry.getAuthor();
  101. String comment = entry.getMessage();
  102. data[ i ][ 0 ] = revision;
  103. data[ i ][ 1 ] = date;
  104. data[ i ][ 2 ] = author;
  105. data[ i ][ 3 ] = comment;
  106. StringBuffer associated_files; // Perforce calls this a "changelist"
  107. if ( showPaths && entry.getChangedPaths().size() > 0 ) {
  108. associated_files = new StringBuffer();
  109. String ls = System.getProperty( "line.separator" );
  110. // entry.changedPaths has the path as a string as the key,
  111. // and an SVNLogEntryPath as the value
  112. Set changedPaths = entry.getChangedPaths().keySet();
  113. for ( Iterator iter = changedPaths.iterator(); iter.hasNext(); ) {
  114. String cp = ( String ) iter.next();
  115. SVNLogEntryPath lep = ( SVNLogEntryPath ) entry.getChangedPaths().get( cp );
  116. if ( lep != null ) {
  117. // type is one of A (added), M (modified), D (deleted), or R (replaced)
  118. // show this along with the path
  119. char type = lep.getType();
  120. associated_files.append( type ).append( " " );
  121. }
  122. associated_files.append( cp ).append( ls );
  123. }
  124. data[ i ][ 4 ] = associated_files.toString();
  125. }
  126. }
  127. String[] col_names = showPaths ?
  128. new String[] {"Revision", "Date", "Author", "Comment", "Paths"} :
  129. new String[] {"Revision", "Date", "Author", "Comment"};
  130. final LogTable table = new LogTable( data, col_names );
  131. table.setPath( path );
  132. table.addMouseListener( new TableMouseListener( table ) );
  133. //ToolTipManager.sharedInstance().registerComponent( table );
  134. // set column widths and cell renderers
  135. TableColumnModel column_model = table.getColumnModel();
  136. TableColumn column0 = column_model.getColumn( 0 ); // revision
  137. column0.setMaxWidth( 60 );
  138. column0.setPreferredWidth( 60 );
  139. column0.setCellRenderer( new TextCellRenderer() );
  140. TableColumn column1 = column_model.getColumn( 1 ); // date
  141. column1.setMaxWidth( 190 );
  142. column1.setPreferredWidth( 190 );
  143. column1.setCellRenderer( new TextCellRenderer() );
  144. TableColumn column2 = column_model.getColumn( 2 ); // author
  145. column2.setMaxWidth( 100 );
  146. column2.setPreferredWidth( 100 );
  147. column2.setCellRenderer( new TextCellRenderer() );
  148. TableColumn column3 = column_model.getColumn( 3 ); // comment
  149. column3.setCellRenderer( new CommentCellRenderer() );
  150. if ( showPaths ) {
  151. TableColumn column4 = column_model.getColumn( 4 ); // paths
  152. column4.setCellRenderer( new PathCellRenderer() );
  153. }
  154. table.packRows();
  155. table.getColumnModel().addColumnModelListener(
  156. new TableColumnModelListener() {
  157. public void columnAdded( TableColumnModelEvent e ) {}
  158. public void columnMarginChanged( ChangeEvent e ) {
  159. table.packRows();
  160. }
  161. public void columnMoved( TableColumnModelEvent e ) {
  162. table.packRows();
  163. }
  164. public void columnRemoved( TableColumnModelEvent e ) {}
  165. public void columnSelectionChanged( ListSelectionEvent e ) {}
  166. }
  167. );
  168. add( label, con );
  169. ++con.y;
  170. add( GUIUtils.createTablePanel( table ), con );
  171. ++con.y;
  172. add( LambdaLayout.createVerticalStrut( 11, true ), con );
  173. ++con.y;
  174. }
  175. }
  176. /**
  177. * Extends JTable to add a path name associated with the table.
  178. */
  179. public static class LogTable extends BestRowTable {
  180. private String path = null;
  181. public LogTable( String[][] rowData, String[] columnNames ) {
  182. super( rowData, columnNames );
  183. }
  184. public void setPath( String path ) {
  185. LogTable.this.path = path;
  186. }
  187. public String getPath() {
  188. return LogTable.this.path;
  189. }
  190. }
  191. /**
  192. * Non-wrapping text area cell renderer.
  193. */
  194. public static class TextCellRenderer extends JTextArea implements TableCellRenderer {
  195. public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column ) {
  196. setText( value == null ? "" : value.toString() );
  197. setBackground( isSelected ? Color.LIGHT_GRAY : Color.WHITE );
  198. return this;
  199. }
  200. }
  201. /**
  202. * Non-wrapping text area cell renderer for the paths column.
  203. */
  204. public static class PathCellRenderer extends JTextArea implements TableCellRenderer {
  205. public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column ) {
  206. setText( value == null ? "" : value.toString() );
  207. //setToolTipText( "<html><b>Other files in this revision:</b><br><pre>" + getText() );
  208. setBackground( isSelected ? Color.LIGHT_GRAY : Color.WHITE );
  209. return this;
  210. }
  211. }
  212. /**
  213. * Wrapping text area cell renderer.
  214. */
  215. public static class CommentCellRenderer extends JTextPane implements TableCellRenderer {
  216. public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column ) {
  217. setText( value == null ? "" : value.toString() );
  218. setBackground( isSelected ? Color.LIGHT_GRAY : Color.WHITE );
  219. return this;
  220. }
  221. }
  222. /**
  223. * for sorting log entries by revision number, latest revision first
  224. */
  225. public static class EntryComparator implements Comparator<SVNLogEntry>, Serializable {
  226. public int compare( SVNLogEntry o1, SVNLogEntry o2 ) {
  227. Long l1 = Long.valueOf( o1.getRevision() );
  228. Long l2 = Long.valueOf( o2.getRevision() );
  229. return l2.compareTo( l1 );
  230. }
  231. }
  232. /**
  233. * MouseListener to popup context menu on the table.
  234. */
  235. class TableMouseListener extends MouseAdapter {
  236. private LogTable table = null;
  237. public TableMouseListener( LogTable table ) {
  238. TableMouseListener.this.table = table;
  239. }
  240. public void mouseReleased( MouseEvent me ) {
  241. handleClick( me );
  242. }
  243. public void mousePressed( MouseEvent me ) {
  244. handleClick( me );
  245. }
  246. private void handleClick( MouseEvent me ) {
  247. if ( me.isPopupTrigger() ) {
  248. Point p = me.getPoint();
  249. int col = table.columnAtPoint( p );
  250. int row = table.rowAtPoint( p );
  251. JPopupMenu popup = getPopupMenu( table, col, row, me.getX(), me.getY() );
  252. if ( popup != null ) {
  253. GUIUtilities.showPopupMenu( popup, table, me.getX(), me.getY() );
  254. }
  255. }
  256. }
  257. }
  258. /**
  259. * Create the context menu.
  260. */
  261. private JPopupMenu getPopupMenu( final LogTable table, final int col, final int row, final int x, final int y) {
  262. final int[] rows = table.getSelectedRows();
  263. JPopupMenu popup = new JPopupMenu();
  264. // if displaying paths, check the individual paths for those starting
  265. // with "D", which indicates a deleted file. Show "Undelete" menu item
  266. // for such paths.
  267. if ( rows.length == 1 && table.getColumnCount() == 5 ) {
  268. String path_value = ( String ) table.getValueAt( rows[ 0 ], 4 );
  269. String[] paths = path_value.split( System.getProperty( "line.separator" ) );
  270. final List<String> deleted_files = new ArrayList<String>();
  271. for ( String p : paths ) {
  272. if ( p != null && p.startsWith( "D " ) ) {
  273. // found a deleted file, add it to the list
  274. deleted_files.add( p.substring( 2 ) );
  275. }
  276. }
  277. if ( !deleted_files.isEmpty() ) {
  278. JMenuItem mi = new JMenuItem( "Undelete" );
  279. popup.add( mi );
  280. mi.addActionListener( new ActionListener() {
  281. public void actionPerformed( ActionEvent ae ) {
  282. try {
  283. // have the user select which files to undelete
  284. UndeleteDialog dialog = new UndeleteDialog( view, deleted_files );
  285. GUIUtils.center( view, dialog );
  286. dialog.setVisible( true );
  287. SVNData data = dialog.getData();
  288. if ( data == null ) {
  289. return ; // null means user canceled
  290. }
  291. // get the repository url and filename of the file to recover
  292. SVNInfo info = logResults.getInfo();
  293. String rep_url_string = info.getRepositoryRootURL().toString();
  294. String file_url_string = info.getURL().toString();
  295. // get the revision to undelete
  296. String revision = ( String ) table.getValueAt( rows[ 0 ], 0 );
  297. // get project root to use as base directory for local destination
  298. String project_root = PVHelper.getProjectRoot( view );
  299. // do the undelete
  300. for ( String remote_filename : data.getPaths() ) {
  301. // remote file
  302. SVNURL rep_url = SVNURL.parseURIDecoded( rep_url_string + remote_filename );
  303. // local filename, extract from remote name
  304. String local_filename = rep_url.toString().substring( file_url_string.length() );
  305. // prep for copy
  306. CopyData copy_data = new CopyData();
  307. copy_data.setSourceURL( rep_url ); // what to copy
  308. copy_data.setRevision( SVNRevision.create( Long.parseLong( revision ) - 1 ) ); // at what revision
  309. copy_data.setDestinationFile( new File( project_root + local_filename ) ); // where to put it
  310. // do the copy
  311. CopyAction action = new CopyAction( view, copy_data );
  312. action.actionPerformed( ae );
  313. }
  314. }
  315. catch ( Exception e ) {
  316. e.printStackTrace();
  317. }
  318. }
  319. }
  320. );
  321. }
  322. }
  323. if ( rows.length == 2 ) {
  324. final String path = table.getPath();
  325. JMenuItem mi = new JMenuItem( "Diff" );
  326. popup.add( mi );
  327. mi.addActionListener( new ActionListener() {
  328. public void actionPerformed( ActionEvent ae ) {
  329. int[] rows = table.getSelectedRows();
  330. String revision1 = ( String ) table.getValueAt( rows[ 0 ], 0 );
  331. String revision2 = ( String ) table.getValueAt( rows[ 1 ], 0 );
  332. DiffAction action = new DiffAction( view, path, revision1, revision2, username, password );
  333. action.actionPerformed( ae );
  334. }
  335. }
  336. );
  337. }
  338. //popup.addSeparator();
  339. JMenuItem mi = new JMenuItem("Zoom");
  340. mi.addActionListener(new ActionListener(){
  341. public void actionPerformed(ActionEvent ae) {
  342. TableCellViewer viewer = new TableCellViewer(table);
  343. viewer.doPopup(col, row, x, y);
  344. }
  345. });
  346. popup.add(mi);
  347. return popup;
  348. }
  349. }