1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * anjuta
4 * Copyright (C) James Liggett 2007 <jrliggett@cox.net>
5 *
6 * anjuta is free software.
7 *
8 * You may redistribute it and/or modify it under the terms of the
9 * GNU General Public License, as published by the Free Software
10 * Foundation; either version 2 of the License, or (at your option)
11 * any later version.
12 *
13 * anjuta is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 * See the GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with anjuta.  If not, write to:
20 * 	The Free Software Foundation, Inc.,
21 * 	51 Franklin Street, Fifth Floor
22 * 	Boston, MA  02110-1301, USA.
23 */
24 
25 #include "anjuta-vcs-status-tree-view.h"
26 #include <glib/gi18n.h>
27 
28 /* A clean way to find out if conflicted items should be selectable */
29 #define IS_SELECTABLE(status, conflicted_selectable) ((status != ANJUTA_VCS_STATUS_CONFLICTED) || \
30 													  (status == ANJUTA_VCS_STATUS_CONFLICTED && \
31 													   conflicted_selectable))
32 
33 enum
34 {
35 	COL_SELECTED,
36 	COL_STATUS,
37 	COL_PATH,
38 	NUM_COLS
39 };
40 
41 enum
42 {
43 	ANJUTA_VCS_STATUS_TREE_VIEW_CONSTRUCT_STATUS_CODES = 1,
44 	ANJUTA_VCS_STATUS_TREE_VIEW_SHOW_STATUS,
45 	ANJUTA_VCS_STATUS_TREE_VIEW_CONSTRUCT_CONFLICTED_SELECTABLE
46 };
47 
48 struct _AnjutaVcsStatusTreeViewPriv
49 {
50 	GtkListStore *store;
51 	guint status_codes;
52 	gboolean show_status;
53 	gboolean conflicted_selectable;
54 };
55 
56 G_DEFINE_TYPE (AnjutaVcsStatusTreeView, anjuta_vcs_status_tree_view,
57 			   GTK_TYPE_TREE_VIEW);
58 
59 static void
on_selected_column_toggled(GtkCellRendererToggle * renderer,gchar * tree_path,AnjutaVcsStatusTreeView * self)60 on_selected_column_toggled (GtkCellRendererToggle *renderer,
61 							gchar *tree_path,
62 							AnjutaVcsStatusTreeView *self)
63 {
64 	GtkTreeIter iter;
65 	gboolean selected;
66 
67 	gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (self->priv->store),
68 										 &iter, tree_path);
69 
70 	gtk_tree_model_get (GTK_TREE_MODEL (self->priv->store), &iter,
71 						COL_SELECTED, &selected, -1);
72 
73 	gtk_list_store_set (self->priv->store, &iter,
74 						COL_SELECTED, !selected, -1);
75 
76 }
77 
78 static void
anjuta_vcs_status_tree_view_status_function(GtkTreeViewColumn * tree_column,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)79 anjuta_vcs_status_tree_view_status_function (GtkTreeViewColumn *tree_column,
80                                              GtkCellRenderer *renderer,
81                                              GtkTreeModel *model,
82                                              GtkTreeIter *iter,
83                                              gpointer user_data)
84 {
85 	AnjutaVcsStatus status;
86 
87 	gtk_tree_model_get (model, iter, COL_STATUS, &status, -1);
88 
89 	switch (status)
90 	{
91 		case ANJUTA_VCS_STATUS_MODIFIED:
92 			g_object_set (G_OBJECT (renderer), "text", _("Modified"), NULL);
93 			break;
94 		case ANJUTA_VCS_STATUS_ADDED:
95 			g_object_set (G_OBJECT (renderer), "text", _("Added"), NULL);
96 			break;
97 		case ANJUTA_VCS_STATUS_DELETED:
98 			g_object_set (G_OBJECT (renderer), "text", _("Deleted"), NULL);
99 			break;
100 		case ANJUTA_VCS_STATUS_CONFLICTED:
101 			g_object_set (G_OBJECT (renderer), "text", _("Conflicted"),
102 									NULL);
103 			break;
104 		case ANJUTA_VCS_STATUS_UPTODATE:
105 			g_object_set (G_OBJECT (renderer), "text", _("Up-to-date"),
106 						  NULL);
107 			break;
108 		case ANJUTA_VCS_STATUS_LOCKED:
109 			g_object_set (G_OBJECT (renderer), "text", _("Locked"), NULL);
110 			break;
111 		case ANJUTA_VCS_STATUS_MISSING:
112 			g_object_set (G_OBJECT (renderer), "text", _("Missing"), NULL);
113 			break;
114 		case ANJUTA_VCS_STATUS_UNVERSIONED:
115 			g_object_set (G_OBJECT (renderer), "text", _("Unversioned"),
116 									NULL);
117 			break;
118 		case ANJUTA_VCS_STATUS_IGNORED:
119 			g_object_set (G_OBJECT (renderer), "text", _("Ignored"),
120 						  NULL);
121 			break;
122 		case ANJUTA_VCS_STATUS_NONE:
123 		default:
124 			break;
125 	}
126 }
127 
128 static void
anjuta_vcs_status_tree_view_icon_function(GtkTreeViewColumn * tree_column,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)129 anjuta_vcs_status_tree_view_icon_function (GtkTreeViewColumn *tree_column,
130                                            GtkCellRenderer *renderer,
131                                            GtkTreeModel *model,
132                                            GtkTreeIter *iter,
133                                            gpointer user_data)
134 {
135 	AnjutaVcsStatus status;
136 
137 	gtk_tree_model_get (model, iter, COL_STATUS, &status, -1);
138 
139 	switch (status)
140 	{
141 		case ANJUTA_VCS_STATUS_MODIFIED:
142 			g_object_set (G_OBJECT (renderer), "stock-id", GTK_STOCK_EDIT,
143 						  NULL);
144 			break;
145 		case ANJUTA_VCS_STATUS_ADDED:
146 			g_object_set (G_OBJECT (renderer), "stock-id", GTK_STOCK_ADD,
147 						  NULL);
148 			break;
149 		case ANJUTA_VCS_STATUS_DELETED:
150 			g_object_set (G_OBJECT (renderer), "stock-id", GTK_STOCK_REMOVE,
151 						  NULL);
152 			break;
153 		case ANJUTA_VCS_STATUS_CONFLICTED:
154 			g_object_set (G_OBJECT (renderer), "stock-id",
155 						  GTK_STOCK_DIALOG_WARNING, NULL);
156 			break;
157 		case ANJUTA_VCS_STATUS_UPTODATE:
158 			g_object_set (G_OBJECT (renderer), "stock-id", GTK_STOCK_APPLY,
159 						  NULL);
160 			break;
161 		case ANJUTA_VCS_STATUS_LOCKED:
162 			g_object_set (G_OBJECT (renderer), "stock-id",
163 						  GTK_STOCK_DIALOG_AUTHENTICATION, NULL);
164 			break;
165 		case ANJUTA_VCS_STATUS_MISSING:
166 			g_object_set (G_OBJECT (renderer), "stock-id",
167 						  GTK_STOCK_MISSING_IMAGE, NULL);
168 			break;
169 		case ANJUTA_VCS_STATUS_UNVERSIONED:
170 			g_object_set (G_OBJECT (renderer), "stock-id",
171 						  GTK_STOCK_DIALOG_QUESTION, NULL);
172 			break;
173 		case ANJUTA_VCS_STATUS_IGNORED:
174 			g_object_set (G_OBJECT (renderer), "stock-id", GTK_STOCK_STOP,
175 						  NULL);
176 			break;
177 		case ANJUTA_VCS_STATUS_NONE:
178 		default:
179 			break;
180 	}
181 }
182 
183 static void
anjuta_vcs_status_tree_view_activatable_function(GtkTreeViewColumn * tree_column,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,AnjutaVcsStatusTreeView * self)184 anjuta_vcs_status_tree_view_activatable_function (GtkTreeViewColumn *tree_column,
185                                          		  GtkCellRenderer *renderer,
186                                          		  GtkTreeModel *model,
187                                          	      GtkTreeIter *iter,
188                                          		  AnjutaVcsStatusTreeView *self)
189 {
190 	AnjutaVcsStatus status;
191 
192 	gtk_tree_model_get (model, iter, COL_STATUS, &status, -1);
193 
194 	g_object_set (G_OBJECT (renderer), "activatable", IS_SELECTABLE (status,
195 																	 self->priv->conflicted_selectable),
196 				  NULL);
197 }
198 
199 static void
anjuta_vcs_status_tree_view_create_columns(AnjutaVcsStatusTreeView * self)200 anjuta_vcs_status_tree_view_create_columns (AnjutaVcsStatusTreeView *self)
201 {
202 	GtkTreeViewColumn *column;
203 	GtkCellRenderer *renderer;
204 
205 	/* Selected column */
206 	column = gtk_tree_view_column_new ();
207 	renderer = gtk_cell_renderer_toggle_new ();
208 	gtk_tree_view_column_pack_start (column, renderer, TRUE);
209 	gtk_tree_view_append_column (GTK_TREE_VIEW (self), column);
210 	gtk_tree_view_column_add_attribute (column, renderer, "active",
211 	                                    COL_SELECTED);
212 	gtk_tree_view_column_set_cell_data_func (column, renderer,
213 											 (GtkTreeCellDataFunc)  anjuta_vcs_status_tree_view_activatable_function,
214 											 self, NULL);
215 
216 	g_signal_connect (G_OBJECT (renderer), "toggled",
217 					  G_CALLBACK (on_selected_column_toggled),
218 					  self);
219 
220 	/* Status column */
221 	column = gtk_tree_view_column_new ();
222 
223 	/* Icon */
224 	renderer = gtk_cell_renderer_pixbuf_new ();
225 	gtk_tree_view_column_pack_start (column, renderer, TRUE);
226 	gtk_tree_view_column_set_cell_data_func (column, renderer,
227 											 (GtkTreeCellDataFunc) anjuta_vcs_status_tree_view_icon_function,
228 											 NULL, NULL);
229 	/* Text */
230 	renderer = gtk_cell_renderer_text_new ();
231 	gtk_tree_view_column_pack_start (column, renderer, TRUE);
232 	gtk_tree_view_append_column (GTK_TREE_VIEW (self), column);
233 	gtk_tree_view_column_set_cell_data_func (column, renderer,
234 											 (GtkTreeCellDataFunc)  anjuta_vcs_status_tree_view_status_function,
235 											 self, NULL);
236 
237 	/* Path column */
238 	column = gtk_tree_view_column_new ();
239 	renderer = gtk_cell_renderer_text_new ();
240 	gtk_tree_view_column_pack_start (column, renderer, TRUE);
241 	gtk_tree_view_append_column (GTK_TREE_VIEW (self), column);
242 	gtk_tree_view_column_add_attribute (column, renderer, "text",
243 										COL_PATH);
244 
245 	gtk_tree_view_set_model (GTK_TREE_VIEW (self),
246 							 GTK_TREE_MODEL (self->priv->store));
247 	g_object_unref (self->priv->store);
248 
249 }
250 
251 static gint
path_sort(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer user_data)252 path_sort (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b,
253 		   gpointer user_data)
254 {
255 	gint compare_value;
256 	gchar *path1;
257 	gchar *path2;
258 
259 	gtk_tree_model_get (model, a, COL_PATH, &path1, -1);
260 	gtk_tree_model_get (model, b, COL_PATH, &path2, -1);
261 
262 	compare_value = strcmp (path1, path2);
263 
264 	g_free (path1);
265 	g_free (path2);
266 
267 	return compare_value;
268 }
269 
270 static void
anjuta_vcs_status_tree_view_init(AnjutaVcsStatusTreeView * self)271 anjuta_vcs_status_tree_view_init (AnjutaVcsStatusTreeView *self)
272 {
273 	GtkTreeSortable *sortable;
274 
275 	self->priv = g_new0 (AnjutaVcsStatusTreeViewPriv, 1);
276 	self->priv->store = gtk_list_store_new (NUM_COLS,
277 											G_TYPE_BOOLEAN,
278 											ANJUTA_TYPE_VCS_STATUS,
279 											G_TYPE_STRING);
280 
281 	anjuta_vcs_status_tree_view_create_columns (self);
282 	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (self), FALSE);
283 
284 	sortable = GTK_TREE_SORTABLE (self->priv->store);
285 	gtk_tree_sortable_set_sort_column_id (sortable, COL_PATH,
286 										  GTK_SORT_ASCENDING);
287 	gtk_tree_sortable_set_sort_func (sortable, COL_PATH, path_sort, NULL,
288 									 NULL);
289 }
290 
291 static void
anjuta_vcs_status_tree_view_finalize(GObject * object)292 anjuta_vcs_status_tree_view_finalize (GObject *object)
293 {
294 	AnjutaVcsStatusTreeView *self;
295 
296 	self = ANJUTA_VCS_STATUS_TREE_VIEW (object);
297 
298 	g_free (self->priv);
299 
300 	G_OBJECT_CLASS (anjuta_vcs_status_tree_view_parent_class)->finalize (object);
301 }
302 
303 static void
anjuta_vcs_status_tree_view_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * param_spec)304 anjuta_vcs_status_tree_view_set_property (GObject *object, guint property_id,
305 										  const GValue *value,
306 										  GParamSpec *param_spec)
307 {
308 	AnjutaVcsStatusTreeView *self;
309 	GtkTreeView *tree_view;
310 	GtkTreeViewColumn *column;
311 
312 	self = ANJUTA_VCS_STATUS_TREE_VIEW (object);
313 
314 	switch (property_id)
315 	{
316 		case ANJUTA_VCS_STATUS_TREE_VIEW_CONSTRUCT_STATUS_CODES:
317 			self->priv->status_codes = g_value_get_flags (value);
318 			break;
319 		case ANJUTA_VCS_STATUS_TREE_VIEW_SHOW_STATUS:
320 			tree_view = GTK_TREE_VIEW (object);
321 			column = gtk_tree_view_get_column (tree_view, COL_STATUS);
322 			self->priv->show_status = g_value_get_boolean (value);
323 
324 			gtk_tree_view_column_set_visible (column, self->priv->show_status);
325 			break;
326 		case ANJUTA_VCS_STATUS_TREE_VIEW_CONSTRUCT_CONFLICTED_SELECTABLE:
327 			self->priv->conflicted_selectable = g_value_get_boolean (value);
328 			break;
329 		default:
330 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
331 			break;
332 	}
333 }
334 
335 static void
anjuta_vcs_status_tree_view_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * param_spec)336 anjuta_vcs_status_tree_view_get_property (GObject *object, guint property_id,
337 										  GValue *value, GParamSpec *param_spec)
338 {
339 	AnjutaVcsStatusTreeView *self;
340 
341 	self = ANJUTA_VCS_STATUS_TREE_VIEW (object);
342 
343 	switch (property_id)
344 	{
345 		case ANJUTA_VCS_STATUS_TREE_VIEW_CONSTRUCT_STATUS_CODES:
346 			g_value_set_flags (value, self->priv->status_codes);
347 			break;
348 		case ANJUTA_VCS_STATUS_TREE_VIEW_SHOW_STATUS:
349 			g_value_set_boolean (value, self->priv->show_status);
350 			break;
351 		case ANJUTA_VCS_STATUS_TREE_VIEW_CONSTRUCT_CONFLICTED_SELECTABLE:
352 			g_value_set_boolean (value, self->priv->conflicted_selectable);
353 			break;
354 		default:
355 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
356 			break;
357 	}
358 }
359 
360 static void
anjuta_vcs_status_tree_view_class_init(AnjutaVcsStatusTreeViewClass * klass)361 anjuta_vcs_status_tree_view_class_init (AnjutaVcsStatusTreeViewClass *klass)
362 {
363 	GObjectClass* object_class = G_OBJECT_CLASS (klass);
364 	GParamSpec *param_spec;
365 
366 	object_class->finalize = anjuta_vcs_status_tree_view_finalize;
367 	object_class->get_property = anjuta_vcs_status_tree_view_get_property;
368 	object_class->set_property = anjuta_vcs_status_tree_view_set_property;
369 
370 	param_spec = g_param_spec_flags ("status-codes", "Status codes",
371 									 "Control which status codes are shown in "
372 									 "the list.",
373 									 ANJUTA_TYPE_VCS_STATUS,
374 									 ANJUTA_VCS_DEFAULT_STATUS_CODES,
375 									 G_PARAM_READWRITE |
376 									 G_PARAM_CONSTRUCT_ONLY);
377 	g_object_class_install_property (object_class,
378 									 ANJUTA_VCS_STATUS_TREE_VIEW_CONSTRUCT_STATUS_CODES,
379 									 param_spec);
380 
381 	param_spec = g_param_spec_boolean ("show-status", "Show status",
382 									   "Show or hide status description",
383 									   TRUE,
384 									   G_PARAM_READWRITE);
385 	g_object_class_install_property (object_class,
386 									 ANJUTA_VCS_STATUS_TREE_VIEW_SHOW_STATUS,
387 									 param_spec);
388 
389 	param_spec = g_param_spec_boolean ("conflicted-selectable",
390 	                                   "Conflicted items selectable",
391 	                                   "Allow/disallow the user to select "
392 	                                   "Conflicted status items.",
393 	                                   TRUE,
394 	                                   G_PARAM_READWRITE |
395 	                                   G_PARAM_CONSTRUCT_ONLY);
396 	g_object_class_install_property (object_class,
397 	                                 ANJUTA_VCS_STATUS_TREE_VIEW_CONSTRUCT_CONFLICTED_SELECTABLE,
398 	                                 param_spec);
399 }
400 
401 
402 GtkWidget *
anjuta_vcs_status_tree_view_new(void)403 anjuta_vcs_status_tree_view_new (void)
404 {
405 	return g_object_new (ANJUTA_VCS_TYPE_STATUS_TREE_VIEW, NULL);
406 }
407 
408 void
anjuta_vcs_status_tree_view_destroy(AnjutaVcsStatusTreeView * self)409 anjuta_vcs_status_tree_view_destroy (AnjutaVcsStatusTreeView *self)
410 {
411 	g_object_unref (self);
412 }
413 
414 void
anjuta_vcs_status_tree_view_add(AnjutaVcsStatusTreeView * self,gchar * path,AnjutaVcsStatus status,gboolean selected)415 anjuta_vcs_status_tree_view_add (AnjutaVcsStatusTreeView *self, gchar *path,
416 								 AnjutaVcsStatus status, gboolean selected)
417 {
418 	GtkTreeIter iter;
419 
420 	if (status & self->priv->status_codes)
421 	{
422 		gtk_list_store_append (self->priv->store, &iter);
423 
424 		gtk_list_store_set (self->priv->store, &iter,
425 							COL_SELECTED, (IS_SELECTABLE (status,
426 														  self->priv->conflicted_selectable) &&
427 														  selected),
428 							COL_STATUS, status,
429 							COL_PATH, path,
430 							-1);
431 	}
432 }
433 
434 static gboolean
select_all_paths(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,AnjutaVcsStatusTreeView * self)435 select_all_paths (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
436 				  AnjutaVcsStatusTreeView *self)
437 {
438 	AnjutaVcsStatus status;
439 
440 	gtk_tree_model_get (model, iter, COL_STATUS, &status, -1);
441 
442 	if (IS_SELECTABLE (status, self->priv->conflicted_selectable))
443 	{
444 		gtk_list_store_set (self->priv->store, iter,
445 							COL_SELECTED, TRUE,
446 							-1);
447 	}
448 
449 	return FALSE;
450 }
451 
452 void
anjuta_vcs_status_tree_view_select_all(AnjutaVcsStatusTreeView * self)453 anjuta_vcs_status_tree_view_select_all (AnjutaVcsStatusTreeView *self)
454 {
455 	gtk_tree_model_foreach (GTK_TREE_MODEL (self->priv->store),
456 						    (GtkTreeModelForeachFunc) select_all_paths,
457 							self);
458 }
459 
460 static gboolean
unselect_all_paths(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,AnjutaVcsStatusTreeView * self)461 unselect_all_paths (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
462 					AnjutaVcsStatusTreeView *self)
463 {
464 	gtk_list_store_set (self->priv->store, iter,
465 						COL_SELECTED, FALSE,
466 						-1);
467 
468 	return FALSE;
469 }
470 
471 void
anjuta_vcs_status_tree_view_unselect_all(AnjutaVcsStatusTreeView * self)472 anjuta_vcs_status_tree_view_unselect_all (AnjutaVcsStatusTreeView *self)
473 {
474 	gtk_tree_model_foreach (GTK_TREE_MODEL (self->priv->store),
475 							(GtkTreeModelForeachFunc) unselect_all_paths,
476 							self);
477 }
478 
479 static gboolean
create_selected_paths_list(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,GList ** list)480 create_selected_paths_list (GtkTreeModel *model, GtkTreePath *path,
481 							GtkTreeIter *iter, GList **list)
482 {
483 	gboolean selected;
484 	gchar *file_path;
485 
486 	gtk_tree_model_get (model, iter,
487 						COL_SELECTED, &selected,
488 						COL_PATH, &file_path,
489 						-1);
490 
491 	if (selected)
492 		*list = g_list_append (*list, g_strdup (file_path));
493 
494 	g_free (file_path);
495 
496 	return FALSE;
497 }
498 
499 GList *
anjuta_vcs_status_tree_view_get_selected(AnjutaVcsStatusTreeView * self)500 anjuta_vcs_status_tree_view_get_selected (AnjutaVcsStatusTreeView *self)
501 {
502 	GList *list;
503 
504 	list = NULL;
505 
506 	gtk_tree_model_foreach (GTK_TREE_MODEL (self->priv->store),
507 							(GtkTreeModelForeachFunc) create_selected_paths_list,
508 							&list);
509 
510 	return list;
511 }
512