git: Fix bgo 687145 - Critical warning when switching git pane
[anjuta.git] / plugins / git / git-branches-pane.c
blobe1cda47ed5e78b4651071f4a9d14cf451a9de85d
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * git-shell-test
4 * Copyright (C) James Liggett 2010 <jrliggett@cox.net>
5 *
6 * git-shell-test 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.
11 * git-shell-test 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.
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/>.
20 #include "git-branches-pane.h"
22 enum
24 COL_SELECTED,
25 COL_ACTIVE,
26 COL_REMOTE,
27 COL_NAME
30 static GtkTargetEntry drag_targets[] =
33 "STRING",
39 struct _GitBranchesPanePriv
41 GtkBuilder *builder;
42 GHashTable *selected_local_branches;
43 GHashTable *selected_remote_branches;
47 G_DEFINE_TYPE (GitBranchesPane, git_branches_pane, GIT_TYPE_PANE);
49 /* The local branch command is started first, usually automatically. Then the
50 * remote branches are listed. We need to have both in the same model, so
51 * the model isn't restored until the remote list command fisishes. */
53 static void
54 on_local_branch_list_command_started (AnjutaCommand *command,
55 GitBranchesPane *self)
57 GtkTreeView *branches_view;
58 GtkListStore *branches_list_model;
60 branches_view = GTK_TREE_VIEW (gtk_builder_get_object (self->priv->builder,
61 "branches_view"));
62 branches_list_model = GTK_LIST_STORE (gtk_builder_get_object (self->priv->builder,
63 "branches_list_model"));
66 gtk_tree_view_set_model (branches_view, NULL);
67 gtk_list_store_clear (branches_list_model);
68 g_hash_table_remove_all (self->priv->selected_local_branches);
69 g_hash_table_remove_all (self->priv->selected_remote_branches);
72 static void
73 on_remote_branch_list_command_finished (AnjutaCommand *command,
74 guint return_code,
75 GitBranchesPane *self)
77 GtkTreeView *branches_view;
78 GtkListStore *branches_list_model;
80 branches_view = GTK_TREE_VIEW (gtk_builder_get_object (self->priv->builder,
81 "branches_view"));
82 branches_list_model = GTK_LIST_STORE (gtk_builder_get_object (self->priv->builder,
83 "branches_list_model"));
85 gtk_tree_view_set_model (branches_view,
86 GTK_TREE_MODEL (branches_list_model));
89 static void
90 on_local_branch_list_command_data_arrived (AnjutaCommand *command,
91 GitBranchesPane *self)
93 GtkListStore *branches_list_model;
94 GList *current_branch;
95 GitBranch *branch;
96 gboolean active;
97 gchar *name;
98 GtkTreeIter iter;
100 branches_list_model = GTK_LIST_STORE (gtk_builder_get_object (self->priv->builder,
101 "branches_list_model"));
102 current_branch = git_branch_list_command_get_output (GIT_BRANCH_LIST_COMMAND (command));
104 while (current_branch)
106 branch = current_branch->data;
107 name = git_branch_get_name (branch);
108 active = git_branch_is_active (branch);
110 gtk_list_store_append (branches_list_model, &iter);
111 gtk_list_store_set (branches_list_model, &iter,
112 COL_SELECTED, FALSE,
113 COL_ACTIVE, active,
114 COL_REMOTE, FALSE,
115 COL_NAME, name,
116 -1);
118 g_free (name);
120 current_branch = g_list_next (current_branch);
124 static void
125 on_remote_branch_list_command_data_arrived (AnjutaCommand *command,
126 GitBranchesPane *self)
128 GtkListStore *branches_list_model;
129 GList *current_branch;
130 GitBranch *branch;
131 gboolean active;
132 gchar *name;
133 GtkTreeIter iter;
135 branches_list_model = GTK_LIST_STORE (gtk_builder_get_object (self->priv->builder,
136 "branches_list_model"));
137 current_branch = git_branch_list_command_get_output (GIT_BRANCH_LIST_COMMAND (command));
139 while (current_branch)
141 branch = current_branch->data;
142 name = git_branch_get_name (branch);
143 active = git_branch_is_active (branch);
145 /* Make sure these entries are treated as remotes */
147 gtk_list_store_append (branches_list_model, &iter);
148 gtk_list_store_set (branches_list_model, &iter,
149 COL_SELECTED, FALSE,
150 COL_ACTIVE, active,
151 COL_REMOTE, TRUE,
152 COL_NAME, name,
153 -1);
155 g_free (name);
157 current_branch = g_list_next (current_branch);
161 static void
162 selected_column_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
163 GtkTreeModel *model, GtkTreeIter *iter,
164 GitBranchesPane *self)
166 gboolean selected;
167 gboolean active;
169 gtk_tree_model_get (model, iter, COL_SELECTED, &selected, COL_ACTIVE,
170 &active, -1);
172 gtk_cell_renderer_toggle_set_active (GTK_CELL_RENDERER_TOGGLE (renderer),
173 selected);
174 gtk_cell_renderer_toggle_set_activatable (GTK_CELL_RENDERER_TOGGLE (renderer),
175 !active);
178 static void
179 active_icon_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
180 GtkTreeModel *model, GtkTreeIter *iter,
181 GitBranchesPane *self)
183 gboolean active;
185 gtk_tree_model_get (model, iter, COL_ACTIVE, &active, -1);
187 if (active)
188 g_object_set (renderer, "stock-id", GTK_STOCK_APPLY, NULL);
189 else
190 g_object_set (renderer, "stock-id", NULL, NULL);
193 static void
194 on_branch_selected_renderer_toggled (GtkCellRendererToggle *renderer,
195 gchar *path, GitBranchesPane *self)
197 GtkTreeModel *branches_list_model;
198 GtkTreeIter iter;
199 gboolean selected;
200 gboolean remote;
201 gchar *name;
202 GHashTable *selection_table;
204 branches_list_model = GTK_TREE_MODEL (gtk_builder_get_object (self->priv->builder,
205 "branches_list_model"));
207 gtk_tree_model_get_iter_from_string (branches_list_model, &iter, path);
208 gtk_tree_model_get (branches_list_model, &iter,
209 COL_SELECTED, &selected,
210 COL_REMOTE, &remote,
211 COL_NAME, &name,
212 -1);
214 selected = !selected;
216 if (remote)
217 selection_table = self->priv->selected_remote_branches;
218 else
219 selection_table = self->priv->selected_local_branches;
221 /* The selection tables are hash sets of each type of selected branch
222 * (local or remote.) The hash table takes ownership of the name string. */
223 if (selected)
224 g_hash_table_insert (selection_table, name, NULL);
225 else
226 g_hash_table_remove (selection_table, name);
228 gtk_list_store_set (GTK_LIST_STORE (branches_list_model), &iter, 0, selected,
229 -1);
232 static void
233 on_branches_list_view_drag_data_get (GtkWidget *branches_list_view,
234 GdkDragContext *drag_context,
235 GtkSelectionData *data,
236 guint info, guint time,
237 GitBranchesPane *self)
239 GtkTreeSelection *selection;
240 GtkTreeIter iter;
241 GtkTreeModel *branches_list_model;
242 gchar *name;
244 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (branches_list_view));
246 if (gtk_tree_selection_count_selected_rows (selection) > 0)
248 gtk_tree_selection_get_selected (selection, &branches_list_model,
249 &iter);
251 gtk_tree_model_get (branches_list_model, &iter, COL_NAME, &name, -1);
253 gtk_selection_data_set_text (data, name, -1);
255 g_free (name);
259 static void
260 selected_branches_table_foreach (gchar *name, gpointer value,
261 GList **list)
263 *list = g_list_append (*list, g_strdup (name));
266 static void
267 git_branches_pane_init (GitBranchesPane *self)
269 gchar *objects[] = {"branches_pane",
270 "branches_list_model",
271 NULL};
272 GError *error = NULL;
273 GtkTreeView *branches_view;
274 GtkTreeViewColumn *branch_selected_column;
275 GtkCellRenderer *branch_selected_renderer;
276 GtkTreeViewColumn *branch_name_column;
277 GtkCellRenderer *branch_active_icon_renderer;
279 self->priv = g_new0 (GitBranchesPanePriv, 1);
280 self->priv->builder = gtk_builder_new ();
281 self->priv->selected_local_branches = g_hash_table_new_full (g_str_hash,
282 g_str_equal,
283 g_free,
284 NULL);
285 self->priv->selected_remote_branches = g_hash_table_new_full (g_str_hash,
286 g_str_equal,
287 g_free,
288 NULL);
291 if (!gtk_builder_add_objects_from_file (self->priv->builder, BUILDER_FILE,
292 objects,
293 &error))
295 g_warning ("Couldn't load builder file: %s", error->message);
296 g_error_free (error);
299 branches_view = GTK_TREE_VIEW (gtk_builder_get_object (self->priv->builder,
300 "branches_view"));
301 branch_selected_column = GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self->priv->builder,
302 "branch_selected_column"));
303 branch_selected_renderer = GTK_CELL_RENDERER (gtk_builder_get_object (self->priv->builder,
304 "branch_selected_renderer"));
305 branch_name_column = GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self->priv->builder,
306 "branch_name_column"));
307 branch_active_icon_renderer = GTK_CELL_RENDERER (gtk_builder_get_object (self->priv->builder,
308 "branch_active_icon_renderer"));
310 /* DND */
311 gtk_tree_view_enable_model_drag_source (branches_view,
312 GDK_BUTTON1_MASK,
313 drag_targets,
314 G_N_ELEMENTS (drag_targets),
315 GDK_ACTION_COPY);
317 g_signal_connect (G_OBJECT (branches_view), "drag-data-get",
318 G_CALLBACK (on_branches_list_view_drag_data_get),
319 self);
321 gtk_tree_view_column_set_cell_data_func (branch_selected_column,
322 branch_selected_renderer,
323 (GtkTreeCellDataFunc) selected_column_data_func,
324 self,
325 NULL);
327 gtk_tree_view_column_set_cell_data_func (branch_name_column,
328 branch_active_icon_renderer,
329 (GtkTreeCellDataFunc) active_icon_data_func,
330 self,
331 NULL);
333 g_signal_connect (G_OBJECT (branch_selected_renderer), "toggled",
334 G_CALLBACK (on_branch_selected_renderer_toggled),
335 self);
338 static void
339 git_branches_pane_finalize (GObject *object)
341 GitBranchesPane *self;
343 self = GIT_BRANCHES_PANE (object);
345 g_object_unref (self->priv->builder);
346 g_hash_table_destroy (self->priv->selected_local_branches);
347 g_hash_table_destroy (self->priv->selected_remote_branches);
348 g_free (self->priv);
350 G_OBJECT_CLASS (git_branches_pane_parent_class)->finalize (object);
353 static GtkWidget *
354 git_branches_pane_get_widget (AnjutaDockPane *pane)
356 GitBranchesPane *self;
358 self = GIT_BRANCHES_PANE (pane);
360 return GTK_WIDGET (gtk_builder_get_object (self->priv->builder,
361 "branches_pane"));
364 static void
365 git_branches_pane_refresh (AnjutaDockPane *pane)
367 /* TODO: Add private function implementation here */
370 static void
371 git_branches_pane_class_init (GitBranchesPaneClass *klass)
373 GObjectClass* object_class = G_OBJECT_CLASS (klass);
374 AnjutaDockPaneClass *pane_class = ANJUTA_DOCK_PANE_CLASS (klass);
376 object_class->finalize = git_branches_pane_finalize;
377 pane_class->get_widget = git_branches_pane_get_widget;
378 pane_class->refresh = git_branches_pane_refresh;
382 AnjutaDockPane *
383 git_branches_pane_new (Git *plugin)
385 GitBranchesPane *self;
387 self = g_object_new (GIT_TYPE_BRANCHES_PANE, "plugin", plugin, NULL);
389 g_signal_connect (G_OBJECT (plugin->local_branch_list_command),
390 "command-started",
391 G_CALLBACK (on_local_branch_list_command_started),
392 self);
394 g_signal_connect (G_OBJECT (plugin->remote_branch_list_command),
395 "command-finished",
396 G_CALLBACK (on_remote_branch_list_command_finished),
397 self);
399 g_signal_connect (G_OBJECT (plugin->local_branch_list_command),
400 "data-arrived",
401 G_CALLBACK (on_local_branch_list_command_data_arrived),
402 self);
404 g_signal_connect (G_OBJECT (plugin->remote_branch_list_command),
405 "data-arrived",
406 G_CALLBACK (on_remote_branch_list_command_data_arrived),
407 self);
409 return ANJUTA_DOCK_PANE (self);
412 GList *
413 git_branches_pane_get_selected_local_branches (GitBranchesPane *self)
415 GList *list;
417 list = NULL;
419 g_hash_table_foreach (self->priv->selected_local_branches,
420 (GHFunc) selected_branches_table_foreach,
421 &list);
423 return list;
426 GList *
427 git_branches_pane_get_selected_remote_branches (GitBranchesPane *self)
429 GList *list;
431 list = NULL;
433 g_hash_table_foreach (self->priv->selected_remote_branches,
434 (GHFunc) selected_branches_table_foreach,
435 &list);
437 return list;
440 gsize
441 git_branches_pane_count_selected_items (GitBranchesPane *self)
443 return (g_hash_table_size (self->priv->selected_local_branches)) +
444 (g_hash_table_size (self->priv->selected_remote_branches));
447 gchar *
448 git_branches_pane_get_selected_branch (GitBranchesPane *self)
450 gchar *selected_branch;
451 GtkTreeView *branches_view;
452 GtkTreeSelection *selection;
453 GtkTreeModel *branches_list_model;
454 GtkTreeIter iter;
456 selected_branch = NULL;
457 branches_view = GTK_TREE_VIEW (gtk_builder_get_object (self->priv->builder,
458 "branches_view"));
459 selection = gtk_tree_view_get_selection (branches_view);
461 if (gtk_tree_selection_count_selected_rows (selection) > 0)
463 gtk_tree_selection_get_selected (selection, &branches_list_model,
464 &iter);
466 gtk_tree_model_get (branches_list_model, &iter, COL_NAME,
467 &selected_branch, -1);
470 return selected_branch;
473 static gboolean
474 clear_branch_selections (GtkTreeModel *model, GtkTreePath *path,
475 GtkTreeIter *iter, gpointer data)
477 gtk_list_store_set (GTK_LIST_STORE (model), iter, COL_SELECTED, FALSE, -1);
479 return FALSE;
482 void
483 git_branches_pane_set_select_column_visible (GitBranchesPane *self,
484 gboolean visible)
486 GtkTreeViewColumn *branch_selected_column;
487 GtkTreeModel *branches_list_model;
489 branch_selected_column = GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self->priv->builder,
490 "branch_selected_column"));
492 gtk_tree_view_column_set_visible (branch_selected_column, visible);
494 /* Clear branch selections when the column becomes invisible again, because
495 * selections have no meaning once an operation that needs these selections
496 * has either been completed or cancelled */
497 if (!visible)
499 branches_list_model = GTK_TREE_MODEL (gtk_builder_get_object (self->priv->builder,
500 "branches_list_model"));
502 gtk_tree_model_foreach (branches_list_model,
503 (GtkTreeModelForeachFunc) clear_branch_selections,
504 NULL);
506 g_hash_table_remove_all (self->priv->selected_local_branches);
507 g_hash_table_remove_all (self->priv->selected_remote_branches);