~ubuntu-branches/ubuntu/quantal/netbeans/quantal

« back to all changes in this revision

Viewing changes to diff/src/org/netbeans/modules/diff/builtin/visualizer/editable/LineNumbersActionsBar.java

  • Committer: Bazaar Package Importer
  • Author(s): Marek Slama
  • Date: 2008-01-29 14:11:22 UTC
  • Revision ID: james.westby@ubuntu.com-20080129141122-fnzjbo11ntghxfu7
Tags: upstream-6.0.1
ImportĀ upstreamĀ versionĀ 6.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 
5
 *
 
6
 * The contents of this file are subject to the terms of either the GNU
 
7
 * General Public License Version 2 only ("GPL") or the Common
 
8
 * Development and Distribution License("CDDL") (collectively, the
 
9
 * "License"). You may not use this file except in compliance with the
 
10
 * License. You can obtain a copy of the License at
 
11
 * http://www.netbeans.org/cddl-gplv2.html
 
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 
13
 * specific language governing permissions and limitations under the
 
14
 * License.  When distributing the software, include this License Header
 
15
 * Notice in each file and include the License file at
 
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 
17
 * particular file as subject to the "Classpath" exception as provided
 
18
 * by Sun in the GPL Version 2 section of the License file that
 
19
 * accompanied this code. If applicable, add the following below the
 
20
 * License Header, with the fields enclosed by brackets [] replaced by
 
21
 * your own identifying information:
 
22
 * "Portions Copyrighted [year] [name of copyright owner]"
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
26
 * The Original Software is NetBeans. The Initial Developer of the Original
 
27
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
 
28
 * Microsystems, Inc. All Rights Reserved.
 
29
 *
 
30
 * If you wish your version of this file to be governed by only the CDDL
 
31
 * or only the GPL Version 2, indicate your decision by adding
 
32
 * "[Contributor] elects to include this software in this distribution
 
33
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 
34
 * single choice of license, a recipient has the option to distribute
 
35
 * your version of this file under either the CDDL, the GPL Version 2 or
 
36
 * to extend the choice of license to its licensees as provided above.
 
37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 
38
 * Version 2 license, then the option applies only if the new code is
 
39
 * made subject to such option by the copyright holder.
 
40
 */
 
41
package org.netbeans.modules.diff.builtin.visualizer.editable;
 
42
 
 
43
import org.netbeans.api.diff.Difference;
 
44
import org.netbeans.editor.*;
 
45
import org.openide.util.NbBundle;
 
46
 
 
47
import javax.swing.*;
 
48
import javax.swing.text.StyledDocument;
 
49
import java.awt.*;
 
50
import java.awt.event.MouseMotionListener;
 
51
import java.awt.event.MouseEvent;
 
52
import java.awt.event.MouseListener;
 
53
import java.awt.geom.Rectangle2D;
 
54
import java.util.*;
 
55
import java.util.List;
 
56
import java.beans.PropertyChangeListener;
 
57
import java.beans.PropertyChangeEvent;
 
58
 
 
59
/**
 
60
 * Draws both line numbers and diff actions for a decorated editor pane.
 
61
 * 
 
62
 * @author Maros Sandor
 
63
 */
 
64
class LineNumbersActionsBar extends JPanel implements Scrollable, MouseMotionListener, MouseListener, PropertyChangeListener {
 
65
 
 
66
    private static final int ACTIONS_BAR_WIDTH = 16;
 
67
    private static final int LINES_BORDER_WIDTH = 4;
 
68
    private static final Point POINT_ZERO = new Point(0, 0);
 
69
    
 
70
    private final Image insertImage = org.openide.util.Utilities.loadImage("org/netbeans/modules/diff/builtin/visualizer/editable/insert.png"); // NOI18N
 
71
    private final Image removeImage = org.openide.util.Utilities.loadImage("org/netbeans/modules/diff/builtin/visualizer/editable/remove.png"); // NOI18N
 
72
 
 
73
    private final Image insertActiveImage = org.openide.util.Utilities.loadImage("org/netbeans/modules/diff/builtin/visualizer/editable/insert_active.png"); // NOI18N
 
74
    private final Image removeActiveImage = org.openide.util.Utilities.loadImage("org/netbeans/modules/diff/builtin/visualizer/editable/remove_active.png"); // NOI18N
 
75
    
 
76
    private final DiffContentPanel master;
 
77
    private final boolean actionsEnabled;
 
78
    private final int actionIconsHeight;
 
79
    private final int actionIconsWidth;
 
80
 
 
81
    private final String  lineNumberPadding = "        "; // NOI18N
 
82
 
 
83
    /**
 
84
     * Rendering hints for annotations sidebar inherited from editor settings.
 
85
     */
 
86
    private Map renderingHints;
 
87
 
 
88
    private int     linesWidth;
 
89
    private int     actionsWidth;
 
90
    
 
91
    private Color   linesColor;
 
92
    private int     linesCount;
 
93
    private int     maxNumberCount;
 
94
 
 
95
    private Point   lastMousePosition = POINT_ZERO;
 
96
    private HotSpot lastHotSpot = null;
 
97
    
 
98
    private List<HotSpot> hotspots = new ArrayList<HotSpot>(0);
 
99
 
 
100
    public LineNumbersActionsBar(DiffContentPanel master, boolean actionsEnabled) {
 
101
        this.master = master;
 
102
        this.actionsEnabled = actionsEnabled;
 
103
        actionsWidth = actionsEnabled ? ACTIONS_BAR_WIDTH : 0;
 
104
        actionIconsHeight = insertImage.getHeight(this);
 
105
        actionIconsWidth = insertImage.getWidth(this);
 
106
        setOpaque(true);
 
107
        setToolTipText(""); // NOI18N
 
108
        master.getMaster().addPropertyChangeListener(this);
 
109
        addMouseMotionListener(this);
 
110
        addMouseListener(this);
 
111
    }
 
112
 
 
113
    public void addNotify() {
 
114
        super.addNotify();
 
115
        initUI();
 
116
    }
 
117
 
 
118
    public void removeNotify() {
 
119
        super.removeNotify();
 
120
    }
 
121
 
 
122
    public void propertyChange(PropertyChangeEvent evt) {
 
123
        repaint();
 
124
    }
 
125
    
 
126
    private Font getLinesFont() {
 
127
        Map coloringMap = EditorUIHelper.getSharedColoringMapFor(master.getEditorPane().getEditorKit().getClass());
 
128
        
 
129
        Object colValue = coloringMap.get(SettingsNames.LINE_NUMBER_COLORING);
 
130
        Coloring col = null;
 
131
        if (colValue != null && colValue instanceof Coloring) {
 
132
            col = (Coloring)colValue;
 
133
        } else {
 
134
            col = SettingsDefaults.defaultLineNumberColoring;
 
135
        }
 
136
        
 
137
        Font font = col.getFont();
 
138
        if (font == null) {
 
139
            font = ((Coloring) coloringMap.get(SettingsNames.DEFAULT_COLORING)).getFont();
 
140
        }
 
141
        return font;
 
142
    }
 
143
    
 
144
    private void initUI() {
 
145
        Map coloringMap = EditorUIHelper.getSharedColoringMapFor(master.getEditorPane().getEditorKit().getClass());
 
146
        
 
147
        Object colValue = coloringMap.get(SettingsNames.LINE_NUMBER_COLORING);
 
148
        Coloring col = null;
 
149
        if (colValue != null && colValue instanceof Coloring) {
 
150
            col = (Coloring)colValue;
 
151
        } else {
 
152
            col = SettingsDefaults.defaultLineNumberColoring;
 
153
        }
 
154
        
 
155
        linesColor = col.getForeColor();
 
156
        if (linesColor == null) {
 
157
            linesColor = ((Coloring) coloringMap.get(SettingsNames.DEFAULT_COLORING)).getForeColor();
 
158
        }
 
159
        Color bg = col.getBackColor();
 
160
        if (bg == null) {
 
161
            bg = ((Coloring) coloringMap.get(SettingsNames.DEFAULT_COLORING)).getBackColor();
 
162
        }
 
163
        setBackground(bg);
 
164
        
 
165
        updateStateOnDocumentChange();
 
166
    }
 
167
 
 
168
    private HotSpot getHotspotAt(Point p) {
 
169
        for (HotSpot hotspot : hotspots) {
 
170
          if (hotspot.getRect().contains(p)) {
 
171
              return hotspot;
 
172
          }
 
173
        }
 
174
        return null;
 
175
    }
 
176
    
 
177
    public String getToolTipText(MouseEvent event) {
 
178
        Point p = event.getPoint();
 
179
        HotSpot spot = getHotspotAt(p);
 
180
        if (spot == null) return null;
 
181
        Difference diff = spot.getDiff();
 
182
        if (diff.getType() == Difference.ADD) {
 
183
            return NbBundle.getMessage(LineNumbersActionsBar.class, "TT_DiffPanel_Remove"); // NOI18N
 
184
        } else if (diff.getType() == Difference.CHANGE) {
 
185
            return NbBundle.getMessage(LineNumbersActionsBar.class, "TT_DiffPanel_Replace"); // NOI18N
 
186
        } else {
 
187
            return NbBundle.getMessage(LineNumbersActionsBar.class, "TT_DiffPanel_Insert"); // NOI18N
 
188
        }
 
189
    }
 
190
 
 
191
    private void performAction(HotSpot spot) {
 
192
        master.getMaster().rollback(spot.getDiff());
 
193
    }
 
194
    
 
195
    public void mouseClicked(MouseEvent e) {
 
196
        if (!e.isPopupTrigger()) {
 
197
            HotSpot spot = getHotspotAt(e.getPoint());
 
198
            if (spot != null) {
 
199
                performAction(spot);
 
200
            }
 
201
        }
 
202
    }
 
203
 
 
204
    public void mousePressed(MouseEvent e) {
 
205
        // not interested
 
206
    }
 
207
 
 
208
    public void mouseReleased(MouseEvent e) {
 
209
        // not interested
 
210
    }
 
211
 
 
212
    public void mouseEntered(MouseEvent e) {
 
213
        // not interested
 
214
    }
 
215
 
 
216
    public void mouseExited(MouseEvent e) {
 
217
        lastMousePosition = POINT_ZERO;
 
218
        if (lastHotSpot != null) {
 
219
            repaint(lastHotSpot.getRect());
 
220
        }
 
221
        lastHotSpot = null;
 
222
    }
 
223
    
 
224
    public void mouseMoved(MouseEvent e) {
 
225
        Point p = e.getPoint();
 
226
        lastMousePosition = p;
 
227
        HotSpot spot = getHotspotAt(p);
 
228
        if (lastHotSpot != spot) {
 
229
            repaint(lastHotSpot == null ? spot.getRect() : lastHotSpot.getRect());
 
230
        }
 
231
        lastHotSpot = spot;
 
232
        setCursor(spot != null ? Cursor.getPredefinedCursor(Cursor.HAND_CURSOR) : Cursor.getDefaultCursor());
 
233
    }
 
234
    
 
235
    public void mouseDragged(MouseEvent e) {
 
236
        // not interested
 
237
    }
 
238
 
 
239
    void onUISettingsChanged() {
 
240
        initUI();        
 
241
        updateStateOnDocumentChange();
 
242
        repaint();
 
243
    }
 
244
    
 
245
    private void updateStateOnDocumentChange() {
 
246
        assert SwingUtilities.isEventDispatchThread();
 
247
        StyledDocument doc = (StyledDocument) master.getEditorPane().getDocument();
 
248
        int lastOffset = doc.getEndPosition().getOffset();
 
249
        linesCount = org.openide.text.NbDocument.findLineNumber(doc, lastOffset);            
 
250
 
 
251
        Graphics g = getGraphics(); 
 
252
        if (g != null) checkLinesWidth(g);
 
253
        maxNumberCount = getNumberCount(linesCount);
 
254
        revalidate();
 
255
    }
 
256
    
 
257
    private int oldLinesWidth;
 
258
    
 
259
    private boolean checkLinesWidth(Graphics g) {
 
260
        FontMetrics fm = g.getFontMetrics(getLinesFont());
 
261
        Rectangle2D rect = fm.getStringBounds(Integer.toString(linesCount), g);
 
262
        linesWidth = (int) rect.getWidth() + LINES_BORDER_WIDTH * 2;
 
263
        if (linesWidth != oldLinesWidth) {
 
264
            oldLinesWidth = linesWidth;
 
265
            revalidate();
 
266
            repaint();
 
267
            return true;
 
268
        }
 
269
        return false;
 
270
    }
 
271
    
 
272
    private int getNumberCount(int n) {
 
273
        int nc = 0;
 
274
        for (; n > 0; n /= 10, nc++);
 
275
        return nc;
 
276
    }
 
277
 
 
278
    public Dimension getPreferredScrollableViewportSize() {
 
279
        Dimension dim = master.getEditorPane().getPreferredScrollableViewportSize();
 
280
        return new Dimension(getBarWidth(), dim.height);
 
281
    }
 
282
 
 
283
    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
 
284
        return master.getEditorPane().getScrollableUnitIncrement(visibleRect, orientation, direction);//123
 
285
    }
 
286
 
 
287
    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
 
288
        return master.getEditorPane().getScrollableBlockIncrement(visibleRect, orientation, direction);
 
289
    }
 
290
 
 
291
    public boolean getScrollableTracksViewportWidth() {
 
292
        return true;
 
293
    }
 
294
 
 
295
    public boolean getScrollableTracksViewportHeight() {
 
296
        return false;
 
297
    }
 
298
    
 
299
    public Dimension getPreferredSize() {
 
300
        return new Dimension(getBarWidth(), Integer.MAX_VALUE >> 2);
 
301
    }
 
302
 
 
303
    private int getBarWidth() {
 
304
        return actionsWidth + linesWidth;
 
305
    }
 
306
 
 
307
    public void onDiffSetChanged() {
 
308
        updateStateOnDocumentChange();
 
309
        repaint();
 
310
    }
 
311
 
 
312
    protected void paintComponent(Graphics gr) {
 
313
        Graphics2D g = (Graphics2D) gr;
 
314
        Rectangle clip = g.getClipBounds();
 
315
        Stroke cs = g.getStroke();
 
316
 
 
317
        if (checkLinesWidth(gr)) return;
 
318
        
 
319
        if (renderingHints == null) {
 
320
            DecoratedEditorPane jep = master.getEditorPane();
 
321
            Class kitClass = jep.getEditorKit().getClass();
 
322
            Object userSetHints = Settings.getValue(kitClass, SettingsNames.RENDERING_HINTS);
 
323
            renderingHints = (userSetHints instanceof Map && ((Map)userSetHints).size() > 0) ? (Map)userSetHints : null;
 
324
        }
 
325
        if (renderingHints != null) {
 
326
            g.addRenderingHints(renderingHints);
 
327
        }
 
328
        
 
329
        EditorUI editorUI = org.netbeans.editor.Utilities.getEditorUI(master.getEditorPane());
 
330
        int lineHeight = editorUI.getLineHeight();
 
331
        
 
332
        g.setColor(getBackground());
 
333
        g.fillRect(clip.x, clip.y, clip.width, clip.height);
 
334
 
 
335
        g.setColor(Color.LIGHT_GRAY);
 
336
        int x = master.isFirst() ? 0 : getBarWidth() - 1;
 
337
        g.drawLine(x, clip.y, x, clip.y + clip.height - 1);
 
338
 
 
339
        DiffViewManager.DecoratedDifference [] diffs = master.getMaster().getManager().getDecorations();
 
340
 
 
341
        int actionsYOffset = (lineHeight - actionIconsHeight) / 2;
 
342
        int offset = linesWidth;
 
343
 
 
344
        int currentDifference = master.getMaster().getCurrentDifference();
 
345
        List<HotSpot> newActionIcons = new ArrayList<HotSpot>();
 
346
        if (master.isFirst()) {
 
347
            int idx = 0;
 
348
            for (DiffViewManager.DecoratedDifference dd : diffs) {
 
349
                g.setColor(master.getMaster().getColorLines());
 
350
                g.setStroke(currentDifference == idx ? master.getMaster().getBoldStroke() : cs);                            
 
351
                g.drawLine(0, dd.getTopLeft(), clip.width, dd.getTopLeft());
 
352
                if (dd.getBottomLeft() != -1) {
 
353
                    g.drawLine(0, dd.getBottomLeft(), clip.width, dd.getBottomLeft());
 
354
                }
 
355
                if (actionsEnabled && dd.canRollback()) {
 
356
                    if (dd.getDiff().getType() != Difference.ADD) {
 
357
                        Rectangle hotSpot = new Rectangle(1, dd.getTopLeft() + actionsYOffset, actionIconsWidth, actionIconsHeight);
 
358
                        if (hotSpot.contains(lastMousePosition) || idx == currentDifference) {
 
359
                            g.drawImage(insertActiveImage, hotSpot.x, hotSpot.y, this);
 
360
                        } else {
 
361
                            g.drawImage(insertImage, hotSpot.x, hotSpot.y, this);
 
362
                        }
 
363
                        newActionIcons.add(new HotSpot(hotSpot, dd.getDiff()));
 
364
                    }
 
365
                }
 
366
                idx++;
 
367
            }
 
368
        } else {
 
369
            int idx = 0;
 
370
            for (DiffViewManager.DecoratedDifference dd : diffs) {
 
371
                g.setColor(master.getMaster().getColorLines());
 
372
                g.setStroke(currentDifference == idx ? master.getMaster().getBoldStroke() : cs);                            
 
373
                g.drawLine(clip.x, dd.getTopRight(), clip.x + clip.width, dd.getTopRight());
 
374
                if (dd.getBottomRight() != -1) {
 
375
                    g.drawLine(clip.x, dd.getBottomRight(), clip.x + clip.width, dd.getBottomRight());
 
376
                }
 
377
                if (actionsEnabled && dd.canRollback()) {
 
378
                    if (dd.getDiff().getType() == Difference.ADD) {
 
379
                        Rectangle hotSpot = new Rectangle(offset + 1, dd.getTopRight() + actionsYOffset, actionIconsWidth, actionIconsHeight);
 
380
                        if (hotSpot.contains(lastMousePosition) || idx == currentDifference) {
 
381
                            g.drawImage(removeActiveImage, hotSpot.x, hotSpot.y, this);
 
382
                        } else {
 
383
                            g.drawImage(removeImage, hotSpot.x, hotSpot.y, this);
 
384
                        }
 
385
                        newActionIcons.add(new HotSpot(hotSpot, dd.getDiff()));
 
386
                    }
 
387
                }
 
388
                idx++;
 
389
            }
 
390
        }
 
391
 
 
392
        hotspots = newActionIcons;
 
393
        
 
394
        int linesXOffset = master.isFirst() ? actionsWidth : 0;
 
395
        linesXOffset += LINES_BORDER_WIDTH;
 
396
        
 
397
        g.setFont(getLinesFont()); 
 
398
        g.setColor(linesColor);
 
399
        int lineNumber = clip.y / lineHeight;
 
400
        int yOffset = lineNumber * lineHeight;
 
401
        yOffset -= lineHeight / 4; // baseline correction
 
402
        int linesDrawn = clip.height / lineHeight + 3;  // draw past clipping rectangle to avoid partially drawn numbers
 
403
        int docLines = Utilities.getRowCount((BaseDocument) master.getEditorPane().getDocument());
 
404
        if (lineNumber + linesDrawn - 1 > docLines) {
 
405
            linesDrawn = docLines - lineNumber + 1;
 
406
        }
 
407
        for (int i = 0; i < linesDrawn; i++) {
 
408
            g.drawString(formatLineNumber(lineNumber), linesXOffset, yOffset);
 
409
            lineNumber++;
 
410
            yOffset += lineHeight;
 
411
        }
 
412
    }
 
413
 
 
414
    private String formatLineNumber(int lineNumber) {
 
415
        String strNumber = Integer.toString(lineNumber);
 
416
        int nc = getNumberCount(lineNumber);
 
417
        if (nc < maxNumberCount) {
 
418
            StringBuilder sb = new StringBuilder(10);
 
419
            sb.append(lineNumberPadding, 0, maxNumberCount - nc);
 
420
            sb.append(strNumber);
 
421
            return sb.toString();
 
422
        } else {
 
423
            return strNumber;
 
424
        }
 
425
    }
 
426
 
 
427
    private static class EditorUIHelper extends EditorUI {
 
428
        /** 
 
429
         * Gets the coloring map that can be shared by the components
 
430
         * with the same kit. Only the component coloring map is provided.
 
431
         */
 
432
        public static Map getSharedColoringMapFor(Class kitClass) {
 
433
            return EditorUIHelper.getSharedColoringMap(kitClass);
 
434
        }
 
435
    }
 
436
}