2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
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]"
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.
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.
41
package org.netbeans.modules.diff.builtin.visualizer.editable;
43
import org.netbeans.api.diff.Difference;
44
import org.netbeans.editor.*;
45
import org.openide.util.NbBundle;
48
import javax.swing.text.StyledDocument;
50
import java.awt.event.MouseMotionListener;
51
import java.awt.event.MouseEvent;
52
import java.awt.event.MouseListener;
53
import java.awt.geom.Rectangle2D;
55
import java.util.List;
56
import java.beans.PropertyChangeListener;
57
import java.beans.PropertyChangeEvent;
60
* Draws both line numbers and diff actions for a decorated editor pane.
62
* @author Maros Sandor
64
class LineNumbersActionsBar extends JPanel implements Scrollable, MouseMotionListener, MouseListener, PropertyChangeListener {
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);
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
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
76
private final DiffContentPanel master;
77
private final boolean actionsEnabled;
78
private final int actionIconsHeight;
79
private final int actionIconsWidth;
81
private final String lineNumberPadding = " "; // NOI18N
84
* Rendering hints for annotations sidebar inherited from editor settings.
86
private Map renderingHints;
88
private int linesWidth;
89
private int actionsWidth;
91
private Color linesColor;
92
private int linesCount;
93
private int maxNumberCount;
95
private Point lastMousePosition = POINT_ZERO;
96
private HotSpot lastHotSpot = null;
98
private List<HotSpot> hotspots = new ArrayList<HotSpot>(0);
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);
107
setToolTipText(""); // NOI18N
108
master.getMaster().addPropertyChangeListener(this);
109
addMouseMotionListener(this);
110
addMouseListener(this);
113
public void addNotify() {
118
public void removeNotify() {
119
super.removeNotify();
122
public void propertyChange(PropertyChangeEvent evt) {
126
private Font getLinesFont() {
127
Map coloringMap = EditorUIHelper.getSharedColoringMapFor(master.getEditorPane().getEditorKit().getClass());
129
Object colValue = coloringMap.get(SettingsNames.LINE_NUMBER_COLORING);
131
if (colValue != null && colValue instanceof Coloring) {
132
col = (Coloring)colValue;
134
col = SettingsDefaults.defaultLineNumberColoring;
137
Font font = col.getFont();
139
font = ((Coloring) coloringMap.get(SettingsNames.DEFAULT_COLORING)).getFont();
144
private void initUI() {
145
Map coloringMap = EditorUIHelper.getSharedColoringMapFor(master.getEditorPane().getEditorKit().getClass());
147
Object colValue = coloringMap.get(SettingsNames.LINE_NUMBER_COLORING);
149
if (colValue != null && colValue instanceof Coloring) {
150
col = (Coloring)colValue;
152
col = SettingsDefaults.defaultLineNumberColoring;
155
linesColor = col.getForeColor();
156
if (linesColor == null) {
157
linesColor = ((Coloring) coloringMap.get(SettingsNames.DEFAULT_COLORING)).getForeColor();
159
Color bg = col.getBackColor();
161
bg = ((Coloring) coloringMap.get(SettingsNames.DEFAULT_COLORING)).getBackColor();
165
updateStateOnDocumentChange();
168
private HotSpot getHotspotAt(Point p) {
169
for (HotSpot hotspot : hotspots) {
170
if (hotspot.getRect().contains(p)) {
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
187
return NbBundle.getMessage(LineNumbersActionsBar.class, "TT_DiffPanel_Insert"); // NOI18N
191
private void performAction(HotSpot spot) {
192
master.getMaster().rollback(spot.getDiff());
195
public void mouseClicked(MouseEvent e) {
196
if (!e.isPopupTrigger()) {
197
HotSpot spot = getHotspotAt(e.getPoint());
204
public void mousePressed(MouseEvent e) {
208
public void mouseReleased(MouseEvent e) {
212
public void mouseEntered(MouseEvent e) {
216
public void mouseExited(MouseEvent e) {
217
lastMousePosition = POINT_ZERO;
218
if (lastHotSpot != null) {
219
repaint(lastHotSpot.getRect());
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());
232
setCursor(spot != null ? Cursor.getPredefinedCursor(Cursor.HAND_CURSOR) : Cursor.getDefaultCursor());
235
public void mouseDragged(MouseEvent e) {
239
void onUISettingsChanged() {
241
updateStateOnDocumentChange();
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);
251
Graphics g = getGraphics();
252
if (g != null) checkLinesWidth(g);
253
maxNumberCount = getNumberCount(linesCount);
257
private int oldLinesWidth;
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;
272
private int getNumberCount(int n) {
274
for (; n > 0; n /= 10, nc++);
278
public Dimension getPreferredScrollableViewportSize() {
279
Dimension dim = master.getEditorPane().getPreferredScrollableViewportSize();
280
return new Dimension(getBarWidth(), dim.height);
283
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
284
return master.getEditorPane().getScrollableUnitIncrement(visibleRect, orientation, direction);//123
287
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
288
return master.getEditorPane().getScrollableBlockIncrement(visibleRect, orientation, direction);
291
public boolean getScrollableTracksViewportWidth() {
295
public boolean getScrollableTracksViewportHeight() {
299
public Dimension getPreferredSize() {
300
return new Dimension(getBarWidth(), Integer.MAX_VALUE >> 2);
303
private int getBarWidth() {
304
return actionsWidth + linesWidth;
307
public void onDiffSetChanged() {
308
updateStateOnDocumentChange();
312
protected void paintComponent(Graphics gr) {
313
Graphics2D g = (Graphics2D) gr;
314
Rectangle clip = g.getClipBounds();
315
Stroke cs = g.getStroke();
317
if (checkLinesWidth(gr)) return;
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;
325
if (renderingHints != null) {
326
g.addRenderingHints(renderingHints);
329
EditorUI editorUI = org.netbeans.editor.Utilities.getEditorUI(master.getEditorPane());
330
int lineHeight = editorUI.getLineHeight();
332
g.setColor(getBackground());
333
g.fillRect(clip.x, clip.y, clip.width, clip.height);
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);
339
DiffViewManager.DecoratedDifference [] diffs = master.getMaster().getManager().getDecorations();
341
int actionsYOffset = (lineHeight - actionIconsHeight) / 2;
342
int offset = linesWidth;
344
int currentDifference = master.getMaster().getCurrentDifference();
345
List<HotSpot> newActionIcons = new ArrayList<HotSpot>();
346
if (master.isFirst()) {
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());
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);
361
g.drawImage(insertImage, hotSpot.x, hotSpot.y, this);
363
newActionIcons.add(new HotSpot(hotSpot, dd.getDiff()));
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());
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);
383
g.drawImage(removeImage, hotSpot.x, hotSpot.y, this);
385
newActionIcons.add(new HotSpot(hotSpot, dd.getDiff()));
392
hotspots = newActionIcons;
394
int linesXOffset = master.isFirst() ? actionsWidth : 0;
395
linesXOffset += LINES_BORDER_WIDTH;
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;
407
for (int i = 0; i < linesDrawn; i++) {
408
g.drawString(formatLineNumber(lineNumber), linesXOffset, yOffset);
410
yOffset += lineHeight;
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();
427
private static class EditorUIHelper extends EditorUI {
429
* Gets the coloring map that can be shared by the components
430
* with the same kit. Only the component coloring map is provided.
432
public static Map getSharedColoringMapFor(Class kitClass) {
433
return EditorUIHelper.getSharedColoringMap(kitClass);