1/*
2 * This file is part of gitg
3 *
4 * Copyright (C) 2015 - Jesse van den Kieboom
5 *
6 * gitg is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * gitg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with gitg. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20class Gitg.DiffViewLinesRenderer : Gtk.SourceGutterRendererText
21{
22	public enum Style
23	{
24		OLD,
25		NEW,
26		SYMBOL
27	}
28
29	private int d_num_digits;
30	private string d_num_digits_fill;
31
32	private ulong d_view_style_updated_id;
33
34	private struct HunkInfo
35	{
36		int start;
37		int end;
38		int hunk_line;
39		Ggit.DiffHunk hunk;
40		string[] line_infos;
41	}
42
43	private Gee.ArrayList<HunkInfo?> d_hunks_list;
44
45	public Style style
46	{
47		get; construct set;
48	}
49
50	private int d_maxlines;
51
52	public int maxlines
53	{
54		get { return d_maxlines; }
55		set
56		{
57			if (value > d_maxlines)
58			{
59				d_maxlines = value;
60
61				calculate_num_digits();
62				recalculate_size();
63			}
64		}
65	}
66
67	public DiffViewLinesRenderer(Style style)
68	{
69		Object(style: style);
70	}
71
72	construct
73	{
74		d_hunks_list = new Gee.ArrayList<HunkInfo?>();
75
76		set_alignment(1.0f, 0.5f);
77		calculate_num_digits();
78	}
79
80	protected Gtk.TextBuffer buffer
81	{
82		get { return get_view().buffer; }
83	}
84
85	protected override void query_data(Gtk.TextIter start, Gtk.TextIter end, Gtk.SourceGutterRendererState state)
86	{
87		var line = start.get_line();
88		bool is_hunk = false;
89		HunkInfo? info = null;
90
91		foreach (var i in d_hunks_list)
92		{
93			if (line == i.hunk_line)
94			{
95				is_hunk = true;
96				break;
97			}
98			else if (line >= i.start && line <= i.end)
99			{
100				info = i;
101				break;
102			}
103		}
104
105		if (info == null || (line - info.start) >= info.line_infos.length)
106		{
107			if (is_hunk && style != Style.SYMBOL)
108			{
109				set_text("...", -1);
110			}
111			else
112			{
113				set_text("", -1);
114			}
115		}
116		else
117		{
118			set_text(info.line_infos[start.get_line() - info.start], -1);
119		}
120	}
121
122	private void on_view_style_updated()
123	{
124		recalculate_size();
125	}
126
127	protected override void change_view(Gtk.TextView? old_view)
128	{
129		if (old_view != null)
130		{
131			old_view.disconnect(d_view_style_updated_id);
132			d_view_style_updated_id = 0;
133		}
134
135		var view = get_view();
136
137		if (view != null)
138		{
139			d_view_style_updated_id = view.style_updated.connect(on_view_style_updated);
140			recalculate_size();
141		}
142
143		base.change_view(old_view);
144	}
145
146	private void recalculate_size()
147	{
148		int size = 0;
149		int height = 0;
150
151		measure(@"$d_num_digits_fill", out size, out height);
152		set_size(size);
153	}
154
155	private void calculate_num_digits()
156	{
157		int num_digits;
158
159		if (style == Style.OLD || style == Style.NEW)
160		{
161			num_digits = 3;
162
163			foreach (var info in d_hunks_list)
164			{
165				var oldn = info.hunk.get_old_start() + info.hunk.get_old_lines();
166				var newn = info.hunk.get_new_start() + info.hunk.get_new_lines();
167
168				var num = int.max(int.max(oldn, newn), d_maxlines);
169
170				var hunk_digits = 0;
171				while (num > 0)
172				{
173					++hunk_digits;
174					num /= 10;
175				}
176
177				num_digits = int.max(num_digits, hunk_digits);
178			}
179		}
180		else
181		{
182			num_digits = 1;
183		}
184
185		d_num_digits = num_digits;
186		d_num_digits_fill = string.nfill(num_digits, ' ');
187	}
188
189	private string[] precalculate_line_strings(Ggit.DiffHunk hunk, Gee.ArrayList<Ggit.DiffLine> lines)
190	{
191		var oldn = hunk.get_old_start();
192		var newn = hunk.get_new_start();
193
194		var lns = lines;
195
196		var line_infos = new string[lns.size];
197
198		for (var i = 0; i < lns.size; i++)
199		{
200			var line = lns[i];
201			var origin = line.get_origin();
202
203			string ltext = "";
204
205			switch (style)
206			{
207			case Style.NEW:
208				if (origin == Ggit.DiffLineType.CONTEXT || origin == Ggit.DiffLineType.ADDITION)
209				{
210					ltext = "%*d".printf(d_num_digits, newn);
211					newn++;
212				}
213				break;
214			case Style.OLD:
215				if (origin == Ggit.DiffLineType.CONTEXT || origin == Ggit.DiffLineType.DELETION)
216				{
217					ltext = "%*d".printf(d_num_digits, oldn);
218					oldn++;
219				}
220				break;
221			case Style.SYMBOL:
222				if (origin == Ggit.DiffLineType.ADDITION)
223				{
224					ltext = "+";
225				}
226				else if (origin == Ggit.DiffLineType.DELETION)
227				{
228					ltext = "-";
229				}
230				break;
231			}
232
233			line_infos[i] = ltext;
234		}
235
236		return line_infos;
237	}
238
239	public void add_hunk(int buffer_line_start, int buffer_line_end, Ggit.DiffHunk hunk, Gee.ArrayList<Ggit.DiffLine> lines)
240	{
241		HunkInfo info = HunkInfo();
242
243		calculate_num_digits();
244
245		info.start = buffer_line_start;
246		info.end = buffer_line_end;
247		info.hunk_line = buffer_line_start - 1;
248		info.hunk = hunk;
249		info.line_infos = precalculate_line_strings(hunk, lines);
250
251		d_hunks_list.add(info);
252
253		recalculate_size();
254	}
255}
256
257// ex:ts=4 noet
258