Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 981656e7a3965e437336aedc72a48ad9241e6c33 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
/*******************************************************************************
 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
 * Copyright (C) 2011, Mathias Kinzler <mathias.kinzler@sap.com>
 * Copyright (C) 2011, Matthias Sohn <matthias.sohn@sap.com>*
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *******************************************************************************/
package org.eclipse.egit.ui.internal.history;

import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.UIPreferences;
import org.eclipse.egit.ui.internal.history.SWTCommitList.SWTLane;
import org.eclipse.jface.resource.ResourceManager;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revplot.AbstractPlotRenderer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.TableItem;

class SWTPlotRenderer extends AbstractPlotRenderer<SWTLane, Color> {

	private static final RGB OUTER_HEAD = new RGB(0, 128, 0);

	private static final RGB INNER_HEAD = new RGB(188, 220, 188);

	private static final RGB OUTER_TAG = new RGB(121, 120, 13);

	private static final RGB INNER_TAG = new RGB(249, 255, 199);

	private static final RGB OUTER_ANNOTATED = new RGB(104, 78, 0);

	private static final RGB INNER_ANNOTATED = new RGB(255, 239, 192);

	private static final RGB OUTER_REMOTE = new RGB(80, 80, 80);

	private static final RGB INNER_REMOTE = new RGB(225, 225, 225);

	private static final RGB OUTER_OTHER = new RGB(30, 30, 30);

	private static final RGB INNER_OTHER = new RGB(250, 250, 250);

	private static final int MAX_LABEL_LENGTH = 18;

	private static final String ELLIPSIS = "\u2026"; // ellipsis "..." (in UTF-8) //$NON-NLS-1$

	private final Color sys_black;

	private final Color sys_gray;

	private final Color sys_white;

	private final Color commitDotFill;

	private final Color commitDotOutline;

	/**
	 * Map from ref name to its label coordinates
	 */
	private final Map<String, Point> labelCoordinates = new HashMap<>();

	/**
	 * Set of ref names that are shown as an ellipsis
	 */
	private final Set<String> ellipsisTags = new HashSet<>();

	private int textHeight;

	private final ResourceManager resources;

	GC g;

	int cellX;

	int cellY;

	Color cellFG;

	Color cellBG;

	private Ref headRef;

	/**
	 * Number of tags of the current commit being rendered. Reset after each
	 * commit.
	 */
	private int tagCount = 0;

	SWTPlotRenderer(final Display d, final ResourceManager resources) {
		this.resources = resources;
		sys_black = d.getSystemColor(SWT.COLOR_BLACK);
		sys_gray = d.getSystemColor(SWT.COLOR_GRAY);
		sys_white = d.getSystemColor(SWT.COLOR_WHITE);

		commitDotFill = resources.createColor(new RGB(220, 220, 220));
		commitDotOutline = resources.createColor(new RGB(110, 110, 110));
	}

	void paint(final Event event, Ref actHeadRef) {
		g = event.gc;

		this.headRef = actHeadRef;
		cellX = event.x;
		cellY = event.y;
		cellFG = g.getForeground();
		cellBG = g.getBackground();
		if (textHeight == 0)
			textHeight = g.stringExtent("/").y; //$NON-NLS-1$

		final TableItem ti = (TableItem) event.item;
		SWTCommit commit = (SWTCommit) ti.getData();
		try {
			commit.parseBody();
		} catch (IOException e) {
			Activator.error("Error parsing body", e); //$NON-NLS-1$
			return;
		}
		paintCommit(commit , event.height);
	}

	@Override
	protected void drawLine(final Color color, final int x1, final int y1,
			final int x2, final int y2, final int width) {
		g.setForeground(color);
		g.setLineWidth(width);
		g.drawLine(cellX + x1, cellY + y1, cellX + x2, cellY + y2);
	}

	protected void drawDot(final Color outline, final Color fill, final int x,
			final int y, final int w, final int h) {
		int dotX = cellX + x + 2;
		int dotY = cellY + y + 1;
		int dotW = w - 2;
		int dotH = h - 2;
		g.setBackground(fill);
		g.fillOval(dotX, dotY, dotW, dotH);
		g.setForeground(outline);
		g.setLineWidth(2);
		g.drawOval(dotX, dotY, dotW, dotH);
	}

	@Override
	protected void drawCommitDot(final int x, final int y, final int w,
			final int h) {
		drawDot(commitDotOutline, commitDotFill, x, y, w, h);
	}

	@Override
	protected void drawBoundaryDot(final int x, final int y, final int w,
			final int h) {
		drawDot(sys_gray, sys_white, x, y, w, h);
	}

	@Override
	protected void drawText(final String msg, final int x, final int y) {
		final Point textsz = g.textExtent(msg);
		final int texty = (y - textsz.y) / 2;
		g.setForeground(cellFG);
		g.setBackground(cellBG);
		g.drawString(msg, cellX + x, cellY + texty, true);

		tagCount = 0;
	}

	@Override
	protected int drawLabel(int x, int y, Ref ref) {
		String txt;
		String name = ref.getName();
		boolean tag = false;
		boolean branch = false;
		RGB labelOuter;
		RGB labelInner;
		if (name.startsWith(Constants.R_HEADS)) {
			branch = true;
			labelOuter = OUTER_HEAD;
			labelInner = INNER_HEAD;
			txt = name.substring(Constants.R_HEADS.length());
		} else if (name.startsWith(Constants.R_REMOTES)) {
			branch = true;
			labelOuter = OUTER_REMOTE;
			labelInner = INNER_REMOTE;
			txt = name.substring(Constants.R_REMOTES.length());
		} else if (name.startsWith(Constants.R_TAGS)) {
			tagCount++;

			tag = true;
			if (ref.getPeeledObjectId() != null) {
				labelOuter = OUTER_ANNOTATED;
				labelInner = INNER_ANNOTATED;
			} else {
				labelOuter = OUTER_TAG;
				labelInner = INNER_TAG;
			}

			int maxNumberOfTags = 1;
			if (tagCount == maxNumberOfTags) {
				txt = name.substring(Constants.R_TAGS.length());
			} else if (tagCount == maxNumberOfTags + 1) {
				txt = ELLIPSIS;
				ellipsisTags.add(name);
			} else {
				// Don't draw additional tags, they are shown when hovering the
				// ellipsis
				return 0;
			}
		} else {
			labelOuter = OUTER_OTHER;
			labelInner = INNER_OTHER;

			if (name.startsWith(Constants.R_REFS))
				txt = name.substring(Constants.R_REFS.length());
			else
				txt = name; // HEAD and such
		}

		int maxLength;
		if (tag)
			maxLength = Activator.getDefault().getPreferenceStore()
					.getInt(UIPreferences.HISTORY_MAX_TAG_LENGTH);
		else if (branch)
			maxLength = Activator.getDefault().getPreferenceStore()
					.getInt(UIPreferences.HISTORY_MAX_BRANCH_LENGTH);
		else
			maxLength = MAX_LABEL_LENGTH;
		if (txt.length() > maxLength) {
			// Account for the ellipsis length
			int textLength = maxLength - 3;
			if (Activator.getDefault().getPreferenceStore()
					.getBoolean(UIPreferences.HISTORY_CUT_AT_START))
				txt = ELLIPSIS + txt.substring(txt.length() - textLength);
			else
				txt = txt.substring(0, textLength) + ELLIPSIS;
		}

		// highlight checked out branch
		Font oldFont = g.getFont();
		boolean isHead = isHead(name);
		if (isHead)
			g.setFont(CommitGraphTable.highlightFont());

		Point textsz = g.stringExtent(txt);
		int arc = textsz.y / 2;
		final int texty = (y * 2 - textsz.y) / 2;
		final int outerWidth = textsz.x + 7;

		// Draw backgrounds
		g.setLineWidth(1);

		g.setBackground(sys_white);
		g.fillRoundRectangle(cellX + x + 1, cellY + texty, textsz.x + 6,
				textsz.y + 1, arc, arc);

		g.setBackground(resources.createColor(labelInner));
		g.fillRoundRectangle(cellX + x + 2, cellY + texty + 1, textsz.x + 4,
				textsz.y - 2, arc - 1, arc - 1);

		g.setForeground(resources.createColor(labelOuter));
		g.drawRoundRectangle(cellX + x, cellY + texty - 1, outerWidth,
				textsz.y + 1, arc, arc);

		g.setForeground(sys_black);

		// Draw text
		g.drawString(txt, cellX + x + 4, cellY + texty, true);

		if (isHead)
			g.setFont(oldFont);

		labelCoordinates.put(name, new Point(x, x + outerWidth));
		return 10 + textsz.x;
	}

	private boolean isHead(String name) {
		boolean isHead = false;
		if (headRef != null) {
			String headRefName = headRef.getLeaf().getName();
			if (name.equals(headRefName))
				isHead = true;
		}
		return isHead;
	}

	@Override
	protected Color laneColor(final SWTLane myLane) {
		return myLane != null ? myLane.color : sys_black;
	}

	/**
	 * Obtain the horizontal span of {@link Ref} label in pixels
	 *
	 * For example, let's assume the SWTCommit has two {@link Ref}s (see
	 * {@link SWTCommit#getRef(int)}, which are rendered as two labels. The
	 * first label may span from 15 to 54 pixels in x direction, while the
	 * second one may span from 58 to 76 pixels.
	 *
	 * This can be used to determine if the mouse is positioned over a label.
	 *
	 * @param ref
	 * @return a Point where x and y designate the start and end x position of
	 *         the label
	 */
	Point getRefHSpan(Ref ref) {
		return labelCoordinates.get(ref.getName());
	}

	boolean isShownAsEllipsis(Ref ref) {
		return ellipsisTags.contains(ref.getName());
	}

	/**
	 * @return text height in pixel
	 */
	public int getTextHeight() {
		return textHeight;
	}
}

Back to the top