1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-  */
2 /*
3  * anjuta-cell-renderer-diff.c
4  * Copyright (C) 2013 James Liggett <jrliggett@cox.net>
5  *
6  * anjuta is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * anjuta is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "anjuta-cell-renderer-diff.h"
21 
22 struct _AnjutaCellRendererDiffPrivate
23 {
24 	GtkCellRenderer *text_cell;
25 };
26 
27 
28 enum
29 {
30 	PROP_0,
31 
32 	PROP_DIFF
33 };
34 
35 
36 /* Line types */
37 typedef enum
38 {
39 	LINE_TYPE_HEADER,
40 	LINE_TYPE_HUNK_HEADER,
41 	LINE_TYPE_ADD,
42 	LINE_TYPE_DELETE,
43 	LINE_TYPE_CONTEXT
44 } LineType;
45 
46 G_DEFINE_TYPE (AnjutaCellRendererDiff, anjuta_cell_renderer_diff, GTK_TYPE_CELL_RENDERER);
47 
48 static void
anjuta_cell_renderer_diff_init(AnjutaCellRendererDiff * self)49 anjuta_cell_renderer_diff_init (AnjutaCellRendererDiff *self)
50 {
51 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ANJUTA_TYPE_CELL_RENDERER_DIFF,
52 	                                          AnjutaCellRendererDiffPrivate);
53 
54 	self->priv->text_cell = gtk_cell_renderer_text_new ();
55 }
56 
57 static void
anjuta_cell_renderer_diff_finalize(GObject * object)58 anjuta_cell_renderer_diff_finalize (GObject *object)
59 {
60 	AnjutaCellRendererDiff *self;
61 
62 	self = ANJUTA_CELL_RENDERER_DIFF (object);
63 
64 	g_object_unref (self->priv->text_cell);
65 
66 	G_OBJECT_CLASS (anjuta_cell_renderer_diff_parent_class)->finalize (object);
67 }
68 
69 static void
anjuta_cell_renderer_diff_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)70 anjuta_cell_renderer_diff_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
71 {
72 	g_return_if_fail (ANJUTA_IS_CELL_RENDERER_DIFF (object));
73 
74 	switch (prop_id)
75 	{
76 	case PROP_DIFF:
77 		anjuta_cell_renderer_diff_set_diff (ANJUTA_CELL_RENDERER_DIFF (object),
78 		                                    g_value_get_string (value));
79 		break;
80 	default:
81 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
82 		break;
83 	}
84 }
85 
86 static void
anjuta_cell_renderer_diff_render(GtkCellRenderer * cell,cairo_t * cr,GtkWidget * widget,const GdkRectangle * background_area,const GdkRectangle * cell_area,GtkCellRendererState flags)87 anjuta_cell_renderer_diff_render (GtkCellRenderer *cell,
88                                   cairo_t *cr,
89                                   GtkWidget *widget,
90                                   const GdkRectangle *background_area,
91                                   const GdkRectangle *cell_area,
92                                   GtkCellRendererState flags)
93 {
94 	AnjutaCellRendererDiff *self;
95 
96 	self = ANJUTA_CELL_RENDERER_DIFF (cell);
97 
98 	gtk_cell_renderer_render (self->priv->text_cell, cr, widget,
99 	                          background_area, cell_area, flags);
100 }
101 
102 static void
anjuta_cell_renderer_diff_get_preferred_width(GtkCellRenderer * cell,GtkWidget * widget,gint * minimum,gint * natural)103 anjuta_cell_renderer_diff_get_preferred_width (GtkCellRenderer *cell,
104                                                GtkWidget *widget,
105                                                gint *minimum,
106                                                gint *natural)
107 {
108 	AnjutaCellRendererDiff *self;
109 
110 	self = ANJUTA_CELL_RENDERER_DIFF (cell);
111 
112 	gtk_cell_renderer_get_preferred_width (self->priv->text_cell, widget,
113 	                                       minimum, natural);
114 }
115 
116 static void
anjuta_cell_renderer_diff_get_preferred_height(GtkCellRenderer * cell,GtkWidget * widget,gint * minimum,gint * natural)117 anjuta_cell_renderer_diff_get_preferred_height (GtkCellRenderer *cell,
118                                                 GtkWidget *widget,
119                                                 gint *minimum,
120                                                 gint *natural)
121 {
122 	AnjutaCellRendererDiff *self;
123 
124 	self = ANJUTA_CELL_RENDERER_DIFF (cell);
125 
126 	gtk_cell_renderer_get_preferred_height (self->priv->text_cell, widget,
127 	                                        minimum, natural);
128 }
129 
130 static void
anjuta_cell_renderer_diff_class_init(AnjutaCellRendererDiffClass * klass)131 anjuta_cell_renderer_diff_class_init (AnjutaCellRendererDiffClass *klass)
132 {
133 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
134 	GtkCellRendererClass *parent_class = GTK_CELL_RENDERER_CLASS (klass);
135 
136 	g_type_class_add_private (klass, sizeof (AnjutaCellRendererDiffPrivate));
137 
138 	object_class->finalize = anjuta_cell_renderer_diff_finalize;
139 	object_class->set_property = anjuta_cell_renderer_diff_set_property;
140 	parent_class->render = anjuta_cell_renderer_diff_render;
141 	parent_class->get_preferred_width = anjuta_cell_renderer_diff_get_preferred_width;;
142 	parent_class->get_preferred_height = anjuta_cell_renderer_diff_get_preferred_height;
143 
144 	g_object_class_install_property (object_class,
145 	                                 PROP_DIFF,
146 	                                 g_param_spec_string ("diff",
147 	                                                      "diff",
148 	                                                      "Diff to render",
149 	                                                      "",
150 	                                                      G_PARAM_WRITABLE));
151 }
152 
153 
154 static PangoAttrList *
create_attribute_list(const gchar * diff)155 create_attribute_list (const gchar *diff)
156 {
157 	PangoAttrList *list;
158 	LineType type;
159 
160 	list = pango_attr_list_new ();
161 
162 	/* Make all of the text monospace */
163 	pango_attr_list_insert (list, pango_attr_family_new ("Monospace"));
164 
165 	/* Assume that diff points to one line of a unified diff */
166 	type = LINE_TYPE_CONTEXT;
167 
168 	if (diff && diff[0])
169 	{
170 		if (*diff != ' ')
171 		{
172 			if (diff[0] == '@' && diff[1] == '@')
173 				type = LINE_TYPE_HUNK_HEADER;
174 			else if (diff[0] == '+')
175 			{
176 				if (g_str_has_prefix (diff, "+++ "))
177 				    type = LINE_TYPE_HEADER;
178 				else
179 					type = LINE_TYPE_ADD;
180 			}
181 			else if (diff[0] == '-')
182 			{
183 				if (g_str_has_prefix (diff, "--- "))
184 					type = LINE_TYPE_HEADER;
185 				else
186 					type = LINE_TYPE_DELETE;
187 			}
188 			else
189 				type = LINE_TYPE_HEADER;
190 		}
191 	}
192 
193 	switch (type)
194 	{
195 		case LINE_TYPE_HEADER:
196 			/* Make file headers easier to see by making them bold */
197 			pango_attr_list_insert (list,
198 			                        pango_attr_weight_new (PANGO_WEIGHT_BOLD));
199 			break;
200 		case LINE_TYPE_HUNK_HEADER:
201 			/* Dark blue */
202 			pango_attr_list_insert (list,
203 			                        pango_attr_foreground_new (0, 0, 0x8000));
204 			break;
205 		case LINE_TYPE_ADD:
206 			/* Dark green */
207 			pango_attr_list_insert (list,
208 			                        pango_attr_foreground_new (0, 0x8000, 0));
209 			break;
210 		case LINE_TYPE_DELETE:
211 			/* Red */
212 			pango_attr_list_insert (list,
213 			                        pango_attr_foreground_new (0xffff, 0, 0));
214 			break;
215 		default:
216 			break;
217 	};
218 
219 	return list;
220 }
221 
222 void
anjuta_cell_renderer_diff_set_diff(AnjutaCellRendererDiff * self,const gchar * diff)223 anjuta_cell_renderer_diff_set_diff (AnjutaCellRendererDiff *self,
224                                     const gchar *diff)
225 {
226 	PangoAttrList *attributes = NULL;
227 	gchar *newline;
228 	gchar *diff_without_newline = NULL;
229 
230 	if (diff)
231 	{
232 		newline = strchr (diff, '\n');
233 
234 		if (newline)
235 		{
236 			diff_without_newline = g_strndup (diff, newline - diff);
237 			g_object_set (G_OBJECT (self->priv->text_cell),
238 			                        "text", diff_without_newline,
239 			                        NULL);
240 			g_free (diff_without_newline);
241 		}
242 		else
243 		{
244 			g_object_set (G_OBJECT (self->priv->text_cell),
245 			              "text", diff,
246 			              NULL);
247 		}
248 
249 		attributes = create_attribute_list (diff);
250 
251 		g_object_set (G_OBJECT (self->priv->text_cell),
252 	          		  "attributes", attributes,
253 		              NULL);
254 
255 		pango_attr_list_unref (attributes);
256 	}
257 	else
258 	{
259 		g_object_set (G_OBJECT (self->priv->text_cell),
260 		              "text", "", NULL);
261 	}
262 }
263 
264 GtkCellRenderer *
anjuta_cell_renderer_diff_new(void)265 anjuta_cell_renderer_diff_new (void)
266 {
267 	return g_object_new (ANJUTA_TYPE_CELL_RENDERER_DIFF, NULL);
268 }
269 
270