1 19 package org.netbeans.modules.subversion.ui.history; 20 21 import org.openide.util.NbBundle; 22 import org.openide.util.RequestProcessor; 23 import org.openide.ErrorManager; 24 import org.openide.windows.TopComponent; 25 import org.openide.nodes.Node; 26 import org.openide.cookies.ViewCookie; 27 import org.openide.filesystems.FileUtil; 28 import org.openide.filesystems.FileObject; 29 import org.netbeans.api.editor.mimelookup.MimeLookup; 30 import org.netbeans.api.editor.settings.FontColorSettings; 31 import org.netbeans.modules.subversion.util.Context; 32 import org.netbeans.modules.subversion.Subversion; 33 import org.netbeans.modules.subversion.RepositoryFile; 34 import org.netbeans.modules.subversion.VersionsCache; 35 import org.netbeans.modules.subversion.client.SvnProgressSupport; 36 import org.netbeans.modules.subversion.ui.update.RevertModifications; 37 import org.netbeans.modules.subversion.ui.update.RevertModificationsAction; 38 import org.netbeans.modules.subversion.ui.diff.DiffSetupSource; 39 import org.netbeans.modules.subversion.ui.diff.ExportDiffAction; 40 import org.tigris.subversion.svnclientadapter.SVNUrl; 41 import org.tigris.subversion.svnclientadapter.SVNRevision; 42 43 import javax.swing.*; 44 import javax.swing.text.*; 45 import java.awt.event.*; 46 import java.awt.*; 47 import java.awt.geom.Rectangle2D ; 48 import java.util.*; 49 import java.util.List ; 50 import java.io.File ; 51 import java.io.IOException ; 52 import java.text.DateFormat ; 53 54 57 62 class SummaryView implements MouseListener, ComponentListener, MouseMotionListener, DiffSetupSource { 63 64 private static final String SUMMARY_REVERT_PROPERTY = "Summary-Revert-"; 65 66 private final SearchHistoryPanel master; 67 68 private JList resultsList; 69 private JScrollPane scrollPane; 70 71 private final List dispResults; 72 private String message; 73 private AttributeSet searchHiliteAttrs; 74 private List <RepositoryRevision> results; 75 76 public SummaryView(SearchHistoryPanel master, List <RepositoryRevision> results) { 77 this.master = master; 78 this.results = results; 79 this.dispResults = expandResults(results); 80 FontColorSettings fcs = (FontColorSettings) MimeLookup.getMimeLookup("text/x-java").lookup(FontColorSettings.class); searchHiliteAttrs = fcs.getFontColors("highlight-search"); message = master.getCriteria().getCommitMessage(); 83 resultsList = new JList(new SummaryListModel()); 84 resultsList.setFixedCellHeight(-1); 85 resultsList.addMouseListener(this); 86 resultsList.addMouseMotionListener(this); 87 resultsList.setCellRenderer(new SummaryCellRenderer()); 88 resultsList.getAccessibleContext().setAccessibleName(NbBundle.getMessage(SummaryView.class, "ACSN_SummaryView_List")); resultsList.getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(SummaryView.class, "ACSD_SummaryView_List")); scrollPane = new JScrollPane(resultsList, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 91 master.addComponentListener(this); 92 } 93 94 public void componentResized(ComponentEvent e) { 95 int [] selection = resultsList.getSelectedIndices(); 96 resultsList.setModel(new SummaryListModel()); 97 resultsList.setSelectedIndices(selection); 98 } 99 100 public void componentHidden(ComponentEvent e) { 101 } 103 104 public void componentMoved(ComponentEvent e) { 105 } 107 108 public void componentShown(ComponentEvent e) { 109 } 111 112 private List expandResults(List <RepositoryRevision> results) { 113 ArrayList newResults = new ArrayList(results.size()); 114 for (RepositoryRevision repositoryRevision : results) { 115 newResults.add(repositoryRevision); 116 List <RepositoryRevision.Event> events = repositoryRevision.getEvents(); 117 for (RepositoryRevision.Event event : events) { 118 newResults.add(event); 119 } 120 } 121 return newResults; 122 } 123 124 public void mouseClicked(MouseEvent e) { 125 int idx = resultsList.locationToIndex(e.getPoint()); 126 if (idx == -1) return; 127 Rectangle rect = resultsList.getCellBounds(idx, idx); 128 Point p = new Point(e.getX() - rect.x, e.getY() - rect.y); 129 Rectangle diffBounds = (Rectangle) resultsList.getClientProperty("Summary-Diff-" + idx); if (diffBounds != null && diffBounds.contains(p)) { 131 diffPrevious(idx); 132 } 133 diffBounds = (Rectangle) resultsList.getClientProperty(SUMMARY_REVERT_PROPERTY + idx); if (diffBounds != null && diffBounds.contains(p)) { 135 revertModifications(new int [] { idx }); 136 } 137 } 138 139 public void mouseEntered(MouseEvent e) { 140 } 142 143 public void mouseExited(MouseEvent e) { 144 } 146 147 public void mousePressed(MouseEvent e) { 148 if (e.isPopupTrigger()) { 149 onPopup(e); 150 } 151 } 152 153 public void mouseReleased(MouseEvent e) { 154 if (e.isPopupTrigger()) { 155 onPopup(e); 156 } 157 } 158 159 public void mouseDragged(MouseEvent e) { 160 } 161 162 public void mouseMoved(MouseEvent e) { 163 int idx = resultsList.locationToIndex(e.getPoint()); 164 if (idx == -1) return; 165 Rectangle rect = resultsList.getCellBounds(idx, idx); 166 Point p = new Point(e.getX() - rect.x, e.getY() - rect.y); 167 Rectangle diffBounds = (Rectangle) resultsList.getClientProperty("Summary-Diff-" + idx); if (diffBounds != null && diffBounds.contains(p)) { 169 resultsList.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 170 return; 171 } 172 diffBounds = (Rectangle) resultsList.getClientProperty(SUMMARY_REVERT_PROPERTY + idx); if (diffBounds != null && diffBounds.contains(p)) { 174 resultsList.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 175 return; 176 } 177 resultsList.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 178 } 179 180 public Collection getSetups() { 181 Node [] nodes = TopComponent.getRegistry().getActivatedNodes(); 182 if (nodes.length == 0) { 183 return master.getSetups(results.toArray(new RepositoryRevision[results.size()]), new RepositoryRevision.Event[0]); 184 } 185 186 Set<RepositoryRevision.Event> events = new HashSet<RepositoryRevision.Event>(); 187 Set<RepositoryRevision> revisions = new HashSet<RepositoryRevision>(); 188 189 int [] sel = resultsList.getSelectedIndices(); 190 for (int i : sel) { 191 Object revCon = dispResults.get(i); 192 if (revCon instanceof RepositoryRevision) { 193 revisions.add((RepositoryRevision) revCon); 194 } else { 195 events.add((RepositoryRevision.Event) revCon); 196 } 197 } 198 return master.getSetups(revisions.toArray(new RepositoryRevision[revisions.size()]), events.toArray(new RepositoryRevision.Event[events.size()])); 199 } 200 201 public String getSetupDisplayName() { 202 return null; 203 } 204 205 private void onPopup(MouseEvent e) { 206 int [] sel = resultsList.getSelectedIndices(); 207 if (sel.length == 0) { 208 int idx = resultsList.locationToIndex(e.getPoint()); 209 if (idx == -1) return; 210 resultsList.setSelectedIndex(idx); 211 sel = new int [] { idx }; 212 } 213 final int [] selection = sel; 214 215 JPopupMenu menu = new JPopupMenu(); 216 217 String previousRevision = null; 218 RepositoryRevision container = null; 219 final RepositoryRevision.Event drev; 220 221 Object revCon = dispResults.get(selection[0]); 222 final boolean revisionSelected; 223 224 if (revCon instanceof RepositoryRevision) { 225 revisionSelected = true; 226 container = (RepositoryRevision) dispResults.get(selection[0]); 227 drev = null; 228 } else { 229 revisionSelected = false; 230 drev = (RepositoryRevision.Event) dispResults.get(selection[0]); 231 container = drev.getLogInfoHeader(); 232 } 233 long revision = container.getLog().getRevision().getNumber(); 234 235 if (revision > 1) { 236 menu.add(new JMenuItem(new AbstractAction(NbBundle.getMessage(SummaryView.class, "CTL_SummaryView_DiffToPrevious", previousRevision)) { { 238 setEnabled(selection.length == 1); 239 } 240 public void actionPerformed(ActionEvent e) { 241 diffPrevious(selection[0]); 242 } 243 })); 244 } 245 246 menu.add(new JMenuItem(new AbstractAction(NbBundle.getMessage(SummaryView.class, "CTL_SummaryView_RollbackChange")) { { 248 setEnabled(true); 249 } 250 public void actionPerformed(ActionEvent e) { 251 revertModifications(selection); 252 } 253 })); 254 255 if (!revisionSelected) { 256 menu.add(new JMenuItem(new AbstractAction(NbBundle.getMessage(SummaryView.class, "CTL_SummaryView_RollbackTo", revision)) { { 258 setEnabled(selection.length == 1 && !revisionSelected); 259 } 260 public void actionPerformed(ActionEvent e) { 261 RequestProcessor.getDefault().post(new Runnable () { 262 public void run() { 263 rollback(drev); 264 } 265 }); 266 } 267 })); 268 menu.add(new JMenuItem(new AbstractAction(NbBundle.getMessage(SummaryView.class, "CTL_SummaryView_View")) { { 270 setEnabled(selection.length == 1 && !revisionSelected && drev.getFile() != null); 271 } 272 public void actionPerformed(ActionEvent e) { 273 RequestProcessor.getDefault().post(new Runnable () { 274 public void run() { 275 view(selection[0]); 276 } 277 }); 278 } 279 })); 280 } 281 282 menu.show(e.getComponent(), e.getX(), e.getY()); 283 } 284 285 290 static void rollback(final RepositoryRevision.Event event) { 291 SVNUrl repository = event.getLogInfoHeader().getRepositoryRootUrl(); 293 RequestProcessor rp = Subversion.getInstance().getRequestProcessor(repository); 294 SvnProgressSupport support = new SvnProgressSupport() { 295 public void perform() { 296 rollback(event, this); 297 } 298 }; 299 support.start(rp, repository, NbBundle.getMessage(SummaryView.class, "MSG_Rollback_Progress")); } 301 302 private static void rollback(RepositoryRevision.Event event, SvnProgressSupport progress) { 303 File file = event.getFile(); 304 File parent = file.getParentFile(); 305 parent.mkdirs(); 306 try { 307 File oldFile = VersionsCache.getInstance().getFileRevision(event.getFile(), Long.toString(event.getLogInfoHeader().getLog().getRevision().getNumber())); 308 file.delete(); 309 FileUtil.copyFile(FileUtil.toFileObject(oldFile), FileUtil.toFileObject(parent), file.getName(), ""); 310 } catch (IOException e) { 311 ErrorManager.getDefault().notify(e); 312 } 313 } 314 315 private void revertModifications(int[] selection) { 316 Set<RepositoryRevision.Event> events = new HashSet<RepositoryRevision.Event>(); 317 Set<RepositoryRevision> revisions = new HashSet<RepositoryRevision>(); 318 for (int idx : selection) { 319 Object o = dispResults.get(idx); 320 if (o instanceof RepositoryRevision) { 321 revisions.add((RepositoryRevision) o); 322 } else { 323 events.add((RepositoryRevision.Event) o); 324 } 325 } 326 revert(master, revisions.toArray(new RepositoryRevision[revisions.size()]), (RepositoryRevision.Event[]) events.toArray(new RepositoryRevision.Event[events.size()])); 327 } 328 329 static void revert(final SearchHistoryPanel master, final RepositoryRevision [] revisions, final RepositoryRevision.Event [] events) { 330 RequestProcessor rp = Subversion.getInstance().getRequestProcessor(master.getSearchRepositoryRootUrl()); 331 SvnProgressSupport support = new SvnProgressSupport() { 332 public void perform() { 333 revertImpl(master, revisions, events, this); 334 } 335 }; 336 support.start(rp, master.getSearchRepositoryRootUrl(), NbBundle.getMessage(SummaryView.class, "MSG_Revert_Progress")); } 338 339 private static void revertImpl(SearchHistoryPanel master, RepositoryRevision[] revisions, RepositoryRevision.Event[] events, SvnProgressSupport progress) { 340 final RepositoryFile repositoryFile = new RepositoryFile(master.getSearchRepositoryRootUrl(), master.getSearchRepositoryRootUrl(), SVNRevision.HEAD); 341 for (RepositoryRevision revision : revisions) { 342 RevertModifications revertModifications = new RevertModifications(repositoryFile, Long.toString(revision.getLog().getRevision().getNumber())); 343 final Context ctx = new Context(master.getRoots()); 344 RevertModificationsAction.performRevert(ctx, revertModifications, progress); 345 } 346 for (RepositoryRevision.Event event : events) { 347 if (event.getFile() == null) continue; 348 RevertModifications revertModifications = new RevertModifications(repositoryFile, Long.toString(event.getLogInfoHeader().getLog().getRevision().getNumber())); 349 final Context ctx = new Context(event.getFile()); 350 RevertModificationsAction.performRevert(ctx, revertModifications, progress); 351 } 352 } 353 354 private void view(int idx) { 355 Object o = dispResults.get(idx); 356 if (o instanceof RepositoryRevision.Event) { 357 RepositoryRevision.Event drev = (RepositoryRevision.Event) o; 358 FileObject fo = FileUtil.toFileObject(drev.getFile()); 359 org.netbeans.modules.versioning.util.Utils.openFile(fo, drev.getLogInfoHeader().getLog().getRevision().toString()); 360 } 361 } 362 private void diffPrevious(int idx) { 363 Object o = dispResults.get(idx); 364 if (o instanceof RepositoryRevision.Event) { 365 RepositoryRevision.Event drev = (RepositoryRevision.Event) o; 366 master.showDiff(drev); 367 } else { 368 RepositoryRevision container = (RepositoryRevision) o; 369 master.showDiff(container); 370 } 371 } 372 373 public JComponent getComponent() { 374 return scrollPane; 375 } 376 377 private class SummaryListModel extends AbstractListModel { 378 379 public int getSize() { 380 return dispResults.size(); 381 } 382 383 public Object getElementAt(int index) { 384 return dispResults.get(index); 385 } 386 } 387 388 private class SummaryCellRenderer extends JPanel implements ListCellRenderer { 389 390 private static final String FIELDS_SEPARATOR = " "; private static final double DARKEN_FACTOR = 0.95; 392 393 private Style selectedStyle; 394 private Style normalStyle; 395 private Style filenameStyle; 396 private Style indentStyle; 397 private Style noindentStyle; 398 private Style hiliteStyle; 399 400 private JTextPane textPane = new JTextPane(); 401 private JPanel actionsPane = new JPanel(); 402 403 private DateFormat defaultFormat; 404 405 private int index; 406 private HyperlinkLabel diffLink; 407 private HyperlinkLabel revertLink; 408 409 public SummaryCellRenderer() { 410 selectedStyle = textPane.addStyle("selected", null); StyleConstants.setForeground(selectedStyle, UIManager.getColor("List.selectionForeground")); normalStyle = textPane.addStyle("normal", null); StyleConstants.setForeground(normalStyle, UIManager.getColor("List.foreground")); filenameStyle = textPane.addStyle("filename", normalStyle); StyleConstants.setBold(filenameStyle, true); 416 indentStyle = textPane.addStyle("indent", null); StyleConstants.setLeftIndent(indentStyle, 50); 418 noindentStyle = textPane.addStyle("noindent", null); StyleConstants.setLeftIndent(noindentStyle, 0); 420 defaultFormat = DateFormat.getDateTimeInstance(); 421 422 hiliteStyle = textPane.addStyle("hilite", normalStyle); Color c = (Color) searchHiliteAttrs.getAttribute(StyleConstants.Background); 424 if (c != null) StyleConstants.setBackground(hiliteStyle, c); 425 c = (Color) searchHiliteAttrs.getAttribute(StyleConstants.Foreground); 426 if (c != null) StyleConstants.setForeground(hiliteStyle, c); 427 428 setLayout(new BorderLayout()); 429 add(textPane); 430 add(actionsPane, BorderLayout.PAGE_END); 431 actionsPane.setLayout(new FlowLayout(FlowLayout.TRAILING, 2, 5)); 432 433 diffLink = new HyperlinkLabel(); 434 diffLink.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 8)); 435 actionsPane.add(diffLink); 436 437 revertLink = new HyperlinkLabel(); 438 actionsPane.add(revertLink); 439 440 textPane.setBorder(null); 441 } 442 443 public Color darker(Color c) { 444 return new Color(Math.max((int)(c.getRed() * DARKEN_FACTOR), 0), 445 Math.max((int)(c.getGreen() * DARKEN_FACTOR), 0), 446 Math.max((int)(c.getBlue() * DARKEN_FACTOR), 0)); 447 } 448 449 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 450 if (value instanceof RepositoryRevision) { 451 renderContainer(list, (RepositoryRevision) value, index, isSelected); 452 } else { 453 renderRevision(list, (RepositoryRevision.Event) value, index, isSelected); 454 } 455 return this; 456 } 457 458 private void renderContainer(JList list, RepositoryRevision container, int index, boolean isSelected) { 459 460 StyledDocument sd = textPane.getStyledDocument(); 461 462 Style style; 463 Color backgroundColor; 464 Color foregroundColor; 465 466 if (isSelected) { 467 foregroundColor = UIManager.getColor("List.selectionForeground"); backgroundColor = UIManager.getColor("List.selectionBackground"); style = selectedStyle; 470 } else { 471 foregroundColor = UIManager.getColor("List.foreground"); backgroundColor = UIManager.getColor("List.background"); backgroundColor = darker(backgroundColor); 474 style = normalStyle; 475 } 476 textPane.setBackground(backgroundColor); 477 actionsPane.setBackground(backgroundColor); 478 479 this.index = index; 480 481 try { 482 sd.remove(0, sd.getLength()); 483 sd.setParagraphAttributes(0, sd.getLength(), noindentStyle, false); 484 485 sd.insertString(0, Long.toString(container.getLog().getRevision().getNumber()), null); 486 sd.setCharacterAttributes(0, sd.getLength(), filenameStyle, false); 487 sd.insertString(sd.getLength(), FIELDS_SEPARATOR + container.getLog().getAuthor(), null); 488 sd.insertString(sd.getLength(), FIELDS_SEPARATOR + defaultFormat.format(container.getLog().getDate()), null); 489 490 String commitMessage = container.getLog().getMessage(); 491 if (commitMessage.endsWith("\n")) commitMessage = commitMessage.substring(0, commitMessage.length() - 1); sd.insertString(sd.getLength(), "\n", null); 493 494 sd.insertString(sd.getLength(), commitMessage, null); 495 496 if (message != null && !isSelected) { 497 int idx = commitMessage.indexOf(message); 498 if (idx != -1) { 499 int len = commitMessage.length(); 500 int doclen = sd.getLength(); 501 sd.setCharacterAttributes(doclen - len + idx, message.length(), hiliteStyle, false); 502 } 503 } 504 505 if (commitMessage != null) { 506 int width = master.getWidth(); 507 if (width > 0) { 508 FontMetrics fm = list.getFontMetrics(list.getFont()); 509 Rectangle2D rect = fm.getStringBounds(commitMessage, textPane.getGraphics()); 510 int nlc, i; 511 for (nlc = -1, i = 0; i != -1 ; i = commitMessage.indexOf('\n', i + 1), nlc++); 512 nlc++; 513 int lines = (int) (rect.getWidth() / (width - 80) + 1); 514 int ph = fm.getHeight() * (lines + nlc) + 0; 515 textPane.setPreferredSize(new Dimension(width - 50, ph)); 516 } 517 } 518 sd.setCharacterAttributes(0, Integer.MAX_VALUE, style, false); 519 } catch (BadLocationException e) { 520 ErrorManager.getDefault().notify(e); 521 } 522 523 actionsPane.setVisible(true); 524 diffLink.set(NbBundle.getMessage(SummaryView.class, "CTL_Action_Diff"), foregroundColor, backgroundColor); 525 revertLink.set(NbBundle.getMessage(SummaryView.class, "CTL_Action_Revert"), foregroundColor, backgroundColor); } 527 528 private void renderRevision(JList list, RepositoryRevision.Event dispRevision, final int index, boolean isSelected) { 529 Style style; 530 StyledDocument sd = textPane.getStyledDocument(); 531 532 Color backgroundColor; 533 Color foregroundColor; 534 535 if (isSelected) { 536 foregroundColor = UIManager.getColor("List.selectionForeground"); backgroundColor = UIManager.getColor("List.selectionBackground"); style = selectedStyle; 539 } else { 540 foregroundColor = UIManager.getColor("List.foreground"); backgroundColor = UIManager.getColor("List.background"); style = normalStyle; 543 } 544 textPane.setBackground(backgroundColor); 545 actionsPane.setVisible(false); 546 547 this.index = -1; 548 try { 549 sd.remove(0, sd.getLength()); 550 sd.setParagraphAttributes(0, sd.getLength(), indentStyle, false); 551 552 sd.insertString(sd.getLength(), String.valueOf(dispRevision.getChangedPath().getAction()), null); 553 sd.insertString(sd.getLength(), FIELDS_SEPARATOR + dispRevision.getChangedPath().getPath(), null); 554 555 sd.setCharacterAttributes(0, Integer.MAX_VALUE, style, false); 556 } catch (BadLocationException e) { 557 ErrorManager.getDefault().notify(e); 558 } 559 } 560 561 protected void paintComponent(Graphics g) { 562 super.paintComponent(g); 563 if (index == -1) return; 564 Rectangle apb = actionsPane.getBounds(); 565 566 { 567 Rectangle bounds = diffLink.getBounds(); 568 bounds.setBounds(bounds.x, bounds.y + apb.y, bounds.width, bounds.height); 569 resultsList.putClientProperty("Summary-Diff-" + index, bounds); } 571 572 Rectangle bounds = revertLink.getBounds(); 573 bounds.setBounds(bounds.x, bounds.y + apb.y, bounds.width, bounds.height); 574 resultsList.putClientProperty(SUMMARY_REVERT_PROPERTY + index, bounds); } 576 } 577 578 private static class HyperlinkLabel extends JLabel { 579 580 public HyperlinkLabel() { 581 setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 582 } 583 584 public void set(String text, Color foreground, Color background) { 585 StringBuilder sb = new StringBuilder (100); 586 if (foreground.equals(UIManager.getColor("List.foreground"))) { sb.append("<html><a HREF=\"\">"); sb.append(text); 589 sb.append("</a>"); } else { 591 sb.append("<html><a HREF=\"\" style=\"color:"); sb.append("rgb("); sb.append(foreground.getRed()); 594 sb.append(","); sb.append(foreground.getGreen()); 596 sb.append(","); sb.append(foreground.getBlue()); 598 sb.append(")"); sb.append("\">"); sb.append(text); 601 sb.append("</a>"); } 603 setText(sb.toString()); 604 setBackground(background); 605 } 606 } 607 } 608 | Popular Tags |