KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > subversion > ui > diff > MultiDiffView


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.netbeans.modules.subversion.ui.diff;
20
21 import java.awt.BorderLayout JavaDoc;
22 import java.awt.Color JavaDoc;
23 import java.awt.Component JavaDoc;
24 import java.awt.Dimension JavaDoc;
25 import java.awt.Graphics JavaDoc;
26 import java.awt.Point JavaDoc;
27 import java.awt.Rectangle JavaDoc;
28 import java.awt.event.*;
29 import java.beans.*;
30 import java.beans.PropertyChangeEvent JavaDoc;
31 import java.beans.PropertyChangeListener JavaDoc;
32 import javax.swing.*;
33 import javax.swing.JToolBar JavaDoc;
34 import javax.swing.event.ChangeEvent JavaDoc;
35 import javax.swing.event.ChangeListener JavaDoc;
36 import org.netbeans.api.diff.DiffView;
37 import org.netbeans.modules.diff.NestableDiffView;
38 import org.openide.util.NbBundle;
39 import org.openide.awt.UndoRedo;
40
41 /**
42  * Subversion diff view that wraps multiple diff
43  * views (representing file content diff and
44  * file properties diffs) into one diff view.
45  *
46  * <p>It reimplements scrollbars handling,
47  * adds changes summary sidebar and under
48  * diff padding.
49  *
50  * <p>It has implementation dependecy on
51  * NestableDiffView.
52  *
53  * @author Petr Kuzel
54  */

55 public class MultiDiffView implements DiffView, PropertyChangeListener JavaDoc {
56
57     /** Views from contructor */
58     private final DiffView[] views;
59
60     /** Elements Initilized on getComponent(). */
61     private final NestableDiffView[] nestableViews;
62
63     /** currentDifference property holder */
64     private int currentDifference;
65
66     private PropertyChangeSupport support;
67
68     /** Container that defines width. */
69     private final Component JavaDoc parent;
70
71     /**
72      * Auxiliary horizontal scrollbar.
73      * Comparing to JScrollPane the MultiDiffView
74      * keeps kontand width and delegates to inner
75      * view JScrollPanes (pair: before, after change).
76      */

77     private final JScrollBar roller = new JScrollBar(JScrollBar.HORIZONTAL);
78
79     /** Maximum inner views width. */
80     private int innerViewsWidth;
81
82     /** The views component. <tt>null</tt> means that component is not initialized, yet */
83     private MultiDiffViewPanel panel;
84
85     /**
86      * Creates a new instance of MultiDiffView
87      *
88      * @param views NestableDiffView
89      * @param parent defines width
90      */

91     public MultiDiffView(DiffView[] views, Component JavaDoc parent) {
92         this.views = views;
93         nestableViews = new NestableDiffView[views.length];
94         for (int i = 0; i<views.length; i++) {
95             views[i].addPropertyChangeListener(this);
96         }
97         this.parent = parent;
98     }
99
100     public Component JavaDoc getComponent() {
101         if (panel == null) {
102             panel = new MultiDiffViewPanel();
103         }
104         return panel;
105     }
106
107     public int getDifferenceCount() {
108         int diffs = 0;
109         for (int i=0; i<views.length; i++) {
110             diffs += views[i].getDifferenceCount();
111         }
112         return diffs;
113     }
114
115     public boolean canSetCurrentDifference() {
116         boolean ret = true;
117         for (int i=0; i<views.length; i++) {
118             ret &= views[i].canSetCurrentDifference();
119         }
120         return ret;
121     }
122
123     /**
124      * Tries to navigate the view to show given difference.
125      */

126     public void setCurrentDifference(int diffNo) throws UnsupportedOperationException JavaDoc {
127         int ypos = 0;
128         for (int i=0; i<views.length; i++) {
129             diffNo -= views[i].getDifferenceCount();
130             if (diffNo < 0) {
131                 int dn = diffNo + views[i].getDifferenceCount();
132                 views[i].setCurrentDifference(dn);
133                 if (nestableViews[i] != null) {
134                     ypos += nestableViews[i].getChangeY(dn);
135                     panel.setViewPosition(new Point JavaDoc(0, ypos));
136                 }
137                 for (int j = 0; j<views.length; j++) {
138                     if (i == j) continue;
139                     views[j].setCurrentDifference(-1);
140                 }
141                 currentDifference = diffNo;
142                 return;
143             }
144             ypos += views[i].getComponent().getHeight();
145         }
146     }
147
148     public int getCurrentDifference() throws UnsupportedOperationException JavaDoc {
149         return currentDifference;
150     }
151
152     public JToolBar JavaDoc getToolBar() {
153         return views[0].getToolBar();
154     }
155
156     public void addPropertyChangeListener(PropertyChangeListener JavaDoc l) {
157         getPropertyChangeSupport().addPropertyChangeListener(l);
158     }
159
160     public void removePropertyChangeListener(PropertyChangeListener JavaDoc l) {
161         getPropertyChangeSupport().removePropertyChangeListener(l);
162     }
163
164     public void propertyChange(PropertyChangeEvent JavaDoc evt) {
165         PropertyChangeEvent JavaDoc event = new PropertyChangeEvent JavaDoc(this, evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
166         getPropertyChangeSupport().firePropertyChange(event);
167     }
168
169     private PropertyChangeSupport getPropertyChangeSupport() {
170         if(support == null) {
171             support = new PropertyChangeSupport(this);
172         }
173         return support;
174     }
175     
176     final static class Padding extends JLabel {
177         final Component JavaDoc view;
178         Padding(Component JavaDoc view) {
179             super(NbBundle.getMessage(MultiDiffView.class, "LBL_Diff_Padding"), SwingConstants.CENTER); // NOI18N
180
this.view = view;
181         }
182         public Dimension JavaDoc getPreferredSize() {
183             return view.getSize();
184         }
185         public Dimension JavaDoc getMinimumSize() {
186             return view.getSize();
187         }
188         public Dimension JavaDoc getMaximumSize() {
189             return view.getSize();
190         }
191     }
192
193     private class MoveAction extends AbstractAction {
194         final int increment;
195         MoveAction(int increment) {
196             this.increment = increment;
197         }
198
199         public void actionPerformed(ActionEvent e) {
200             int value = roller.getValue() + increment;
201             if (roller.getMinimum() < value && value < roller.getMaximum()) {
202                 roller.setValue(value);
203             }
204         }
205     }
206
207     private class MultiDiffViewPanel extends JPanel implements ChangeListener JavaDoc {
208         
209         /** Scroll pane hosting nested diff views. */
210         private JScrollPane pane;
211     
212         MultiDiffViewPanel() {
213             setFocusable(false);
214             
215             ScrollablePanel scrollablePanel = new ScrollablePanel();
216
217             pane = new JScrollPane();
218             pane.setBorder(BorderFactory.createEmptyBorder());
219             pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
220             pane.setViewportView(scrollablePanel);
221
222             for (int i=0; i<views.length; i++) {
223                 JComponent c = (JComponent) views[i].getComponent();
224                 if (i == 0) putClientProperty(UndoRedo.class, c.getClientProperty(UndoRedo.class));
225                 scrollablePanel.add(c);
226                 if (views[i] instanceof NestableDiffView) {
227                     c.putClientProperty(NestableDiffView.class, views[i]);
228                     NestableDiffView impl = (NestableDiffView) views[i];
229
230                     // register keystrokes with roller
231
KeyStroke stroke;
232                     Action action;
233                     Object JavaDoc paneKey;
234
235                     stroke = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0);
236                     action = new MoveAction(-10);
237                     paneKey = pane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).get(stroke);
238                     pane.getActionMap().put(paneKey, action);
239
240                     stroke = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0);
241                     action = new MoveAction(10);
242                     paneKey = pane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).get(stroke);
243                     pane.getActionMap().put(paneKey, action);
244
245                     impl.joinScrollPane(pane);
246                     nestableViews[i] = impl;
247                 }
248             }
249
250             // add extra panel that allows to show the last file change at view top
251
// it's more convenient for reading to have all
252
// changes positioned at top
253
scrollablePanel.add(new Padding(parent));
254             
255             setLayout(new BorderLayout JavaDoc());
256             add(pane, BorderLayout.CENTER);
257             add(new SummarySideBar(), BorderLayout.LINE_END);
258
259             JScrollBar vertical = new JScrollBar(JScrollBar.VERTICAL);
260             int padding = vertical.getPreferredSize().width + SUMMARY_WIDTH;
261             roller.setBorder(BorderFactory.createEmptyBorder(0,0,0,padding));
262             add(roller, BorderLayout.SOUTH);
263         }
264         
265         public void layout() {
266             layoutImpl();
267         }
268
269         public void doLayout() {
270             layoutImpl();
271         }
272
273         void layoutImpl() {
274             // emulate vertical scrollbar that delegates to
275
// inner views
276
super.layout();
277
278             for (int i = 0; i<nestableViews.length; i++) {
279                 innerViewsWidth = Math.max(innerViewsWidth, nestableViews[i].getInnerWidth());
280             }
281
282             for (int i = 0; i<nestableViews.length; i++) {
283                 nestableViews[i].setInnerWidth(innerViewsWidth);
284             }
285
286             BoundedRangeModel model = roller.getModel();
287             if (innerViewsWidth > viewWidth()){
288                 roller.setVisible(true);
289                 afterResize();
290                 model.addChangeListener(this);
291             } else {
292                 roller.setVisible(false);
293                 model.removeChangeListener(this);
294             }
295         }
296         
297         // hook into resize(), setSize() methods
298
void afterResize() {
299             final BoundedRangeModel model = roller.getModel();
300             model.setMaximum(innerViewsWidth);
301             model.setExtent(viewWidth());
302             model.setValue(0);
303         }
304
305         int viewWidth() {
306             JScrollBar vertical = new JScrollBar(JScrollBar.VERTICAL);
307             int padding = vertical.getPreferredSize().width + SUMMARY_WIDTH;
308             return parent.getWidth() - padding;
309         }
310         
311         public void stateChanged(ChangeEvent JavaDoc e) {
312             BoundedRangeModel model = roller.getModel();
313             if (e.getSource() == model) {
314                 if (model.getValueIsAdjusting()) {
315                     //return;
316
}
317                 for (int i = 0; i<nestableViews.length; i++) {
318                     nestableViews[i].setHorizontalPosition(model.getValue());
319                 }
320             }
321         }
322
323         public boolean requestFocusInWindow() {
324             // XXX I was not able to achieve it declaratively by setting focusable, root, ... properties
325
// the focus usually stayed at parent's upper toolbar or nowhere
326
return pane.requestFocusInWindow();
327         }
328         
329         void setViewPosition(Point JavaDoc p) {
330             pane.getViewport().setViewPosition(p);
331         }
332         
333     }
334     
335     private class ScrollablePanel extends JPanel implements Scrollable {
336         
337         ScrollablePanel() {
338             super();
339             setLayout(new VerticalFlowLayout(parent));
340             setFocusable(false);
341         }
342         
343         public Dimension JavaDoc getPreferredScrollableViewportSize() {
344             return getPreferredSize();
345         }
346
347         public int getScrollableUnitIncrement(Rectangle JavaDoc visibleRect, int orientation, int direction) {
348             JComponent c = (JComponent) getComponent(0);
349             NestableDiffView inner = (NestableDiffView) c.getClientProperty(NestableDiffView.class);
350             return inner.getInnerScrollableUnitIncrement(visibleRect, orientation, direction);
351         }
352
353         public int getScrollableBlockIncrement(Rectangle JavaDoc visibleRect, int orientation, int direction) {
354             JComponent c = (JComponent) getComponent(0);
355             NestableDiffView inner = (NestableDiffView) c.getClientProperty(NestableDiffView.class);
356             return inner.getInnerScrollableBlockIncrement(visibleRect, orientation, direction);
357         }
358
359         public boolean getScrollableTracksViewportWidth() {
360             return false; // XXX what does it mean? Documented in Scrollable?
361
}
362
363         public boolean getScrollableTracksViewportHeight() {
364             return false; // XXX what does it mean? Documented in Scrollable?
365
}
366     }
367
368     final int SUMMARY_WIDTH = 12;
369     final int SCROLLBAR_TOP = 12; //XXX height of top scrollbar arrow, count it dynamically, but it depends on L&F
370
final int SCROLLBAR_DOWN = 12; //XXX height of bottom scrollbar arrow, count it dynamically, but it depends on L&F
371

372     class SummarySideBar extends JComponent {
373
374         public Dimension JavaDoc getPreferredSize() {
375             return new Dimension JavaDoc(SUMMARY_WIDTH, Integer.MAX_VALUE);
376         }
377
378         protected void paintComponent(Graphics JavaDoc g) {
379             super.paintComponent(g);
380
381             Color JavaDoc color = g.getColor();
382             g.setColor(Color.BLUE);
383             int ybase = 0;
384             float myHeight = getHeight() - SCROLLBAR_TOP - SCROLLBAR_DOWN;
385             float ratio = myHeight / ((float) panel.getHeight());
386             for (int i = 0; i<views.length; i++) {
387                 if (nestableViews[i] == null) {
388                     continue;
389                 }
390
391                 for (int x = 0; x<views[i].getDifferenceCount(); x++) {
392                     int ypos = nestableViews[i].getChangeY(x);
393                     int y = SCROLLBAR_TOP + (int)((ypos + ybase) * ratio);
394                     g.drawRect(0, y, SUMMARY_WIDTH, 1);
395                 }
396                 ybase += views[i].getComponent().getHeight();
397             }
398             g.setColor(color);
399         }
400     }
401 }
402
Popular Tags