code-analyzer: Fixed bgo#667903 - Code Analyzer Crashes
[anjuta.git] / plugins / git / git-branches-pane.c
blob468e126cd60af408c27b6f67f5232d6d5d478195
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;
45 GtkAction *merge_action;
46 GtkAction *delete_action;
47 GtkAction *switch_action;
51 G_DEFINE_TYPE (GitBranchesPane, git_branches_pane, GIT_TYPE_PANE);
53 /* The local branch command is started first, usually automatically. Then the
54 * remote branches are listed. We need to have both in the same model, so
55 * the model isn't restored until the remote list command fisishes. */
57 static void
58 on_local_branch_list_command_started (AnjutaCommand *command,
59 GitBranchesPane *self)
61 GtkTreeView *branches_view;
62 GtkListStore *branches_list_model;
64 branches_view = GTK_TREE_VIEW (gtk_builder_get_object (self->priv->builder,
65 "branches_view"));
66 branches_list_model = GTK_LIST_STORE (gtk_builder_get_object (self->priv->builder,
67 "branches_list_model"));
70 gtk_tree_view_set_model (branches_view, NULL);
71 gtk_list_store_clear (branches_list_model);
72 g_hash_table_remove_all (self->priv->selected_local_branches);
73 g_hash_table_remove_all (self->priv->selected_remote_branches);
76 static void
77 on_remote_branch_list_command_finished (AnjutaCommand *command,
78 guint return_code,
79 GitBranchesPane *self)
81 GtkTreeView *branches_view;
82 GtkListStore *branches_list_model;
84 branches_view = GTK_TREE_VIEW (gtk_builder_get_object (self->priv->builder,
85 "branches_view"));
86 branches_list_model = GTK_LIST_STORE (gtk_builder_get_object (self->priv->builder,
87 "branches_list_model"));
89 gtk_tree_view_set_model (branches_view,
90 GTK_TREE_MODEL (branches_list_model));
93 static void
94 on_local_branch_list_command_data_arrived (AnjutaCommand *command,
95 GitBranchesPane *self)
97 GtkListStore *branches_list_model;
98 GList *current_branch;
99 GitBranch *branch;
100 gboolean active;
101 gchar *name;
102 GtkTreeIter iter;
104 branches_list_model = GTK_LIST_STORE (gtk_builder_get_object (self->priv->builder,
105 "branches_list_model"));
106 current_branch = git_branch_list_command_get_output (GIT_BRANCH_LIST_COMMAND (command));
108 while (current_branch)
110 branch = current_branch->data;
111 name = git_branch_get_name (branch);
112 active = git_branch_is_active (branch);
114 gtk_list_store_append (branches_list_model, &iter);
115 gtk_list_store_set (branches_list_model, &iter,
116 COL_SELECTED, FALSE,
117 COL_ACTIVE, active,
118 COL_REMOTE, FALSE,
119 COL_NAME, name,
120 -1);
122 g_free (name);
124 current_branch = g_list_next (current_branch);
128 static void
129 on_remote_branch_list_command_data_arrived (AnjutaCommand *command,
130 GitBranchesPane *self)
132 GtkListStore *branches_list_model;
133 GList *current_branch;
134 GitBranch *branch;
135 gboolean active;
136 gchar *name;
137 GtkTreeIter iter;
139 branches_list_model = GTK_LIST_STORE (gtk_builder_get_object (self->priv->builder,
140 "branches_list_model"));
141 current_branch = git_branch_list_command_get_output (GIT_BRANCH_LIST_COMMAND (command));
143 while (current_branch)
145 branch = current_branch->data;
146 name = git_branch_get_name (branch);
147 active = git_branch_is_active (branch);
149 /* Make sure these entries are treated as remotes */
151 gtk_list_store_append (branches_list_model, &iter);
152 gtk_list_store_set (branches_list_model, &iter,
153 COL_SELECTED, FALSE,
154 COL_ACTIVE, active,
155 COL_REMOTE, TRUE,
156 COL_NAME, name,
157 -1);
159 g_free (name);
161 current_branch = g_list_next (current_branch);
165 static void
166 selected_column_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
167 GtkTreeModel *model, GtkTreeIter *iter,
168 GitBranchesPane *self)
170 gboolean selected;
171 gboolean active;
173 gtk_tree_model_get (model, iter, COL_SELECTED, &selected, COL_ACTIVE,
174 &active, -1);
176 gtk_cell_renderer_toggle_set_active (GTK_CELL_RENDERER_TOGGLE (renderer),
177 selected);
178 gtk_cell_renderer_toggle_set_activatable (GTK_CELL_RENDERER_TOGGLE (renderer),
179 !active);
182 static void
183 active_icon_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
184 GtkTreeModel *model, GtkTreeIter *iter,
185 GitBranchesPane *self)
187 gboolean active;
189 gtk_tree_model_get (model, iter, COL_ACTIVE, &active, -1);
191 if (active)
192 g_object_set (renderer, "stock-id", GTK_STOCK_APPLY, NULL);
193 else
194 g_object_set (renderer, "stock-id", "", NULL);
197 static void
198 on_branch_selected_renderer_toggled (GtkCellRendererToggle *renderer,
199 gchar *path, GitBranchesPane *self)
201 GtkTreeModel *branches_list_model;
202 GtkTreeIter iter;
203 gboolean selected;
204 gboolean remote;
205 gchar *name;
206 GHashTable *selection_table;
208 branches_list_model = GTK_TREE_MODEL (gtk_builder_get_object (self->priv->builder,
209 "branches_list_model"));
211 gtk_tree_model_get_iter_from_string (branches_list_model, &iter, path);
212 gtk_tree_model_get (branches_list_model, &iter,
213 COL_SELECTED, &selected,
214 COL_REMOTE, &remote,
215 COL_NAME, &name,
216 -1);
218 selected = !selected;
220 if (remote)
221 selection_table = self->priv->selected_remote_branches;
222 else
223 selection_table = self->priv->selected_local_branches;
225 /* The selection tables are hash sets of each type of selected branch
226 * (local or remote.) The hash table takes ownership of the name string. */
227 if (selected)
228 g_hash_table_insert (selection_table, name, NULL);
229 else
230 g_hash_table_remove (selection_table, name);
232 gtk_list_store_set (GTK_LIST_STORE (branches_list_model), &iter, 0, selected,
233 -1);
234 git_branches_pane_update_ui (self);
237 static void
238 on_branches_list_view_drag_data_get (GtkWidget *branches_list_view,
239 GdkDragContext *drag_context,
240 GtkSelectionData *data,
241 guint info, guint time,
242 GitBranchesPane *self)
244 GtkTreeSelection *selection;
245 GtkTreeIter iter;
246 GtkTreeModel *branches_list_model;
247 gchar *name;
249 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (branches_list_view));
251 if (gtk_tree_selection_count_selected_rows (selection) > 0)
253 gtk_tree_selection_get_selected (selection, &branches_list_model,
254 &iter);
256 gtk_tree_model_get (branches_list_model, &iter, COL_NAME, &name, -1);
258 gtk_selection_data_set_text (data, name, -1);
260 g_free (name);
264 static void
265 selected_branches_table_foreach (gchar *name, gpointer value,
266 GList **list)
268 *list = g_list_append (*list, g_strdup (name));
271 static void
272 git_branches_pane_init (GitBranchesPane *self)
274 gchar *objects[] = {"branches_pane",
275 "branches_list_model",
276 NULL};
277 GError *error = NULL;
278 GtkTreeView *branches_view;
279 GtkTreeViewColumn *branch_selected_column;
280 GtkCellRenderer *branch_selected_renderer;
281 GtkTreeViewColumn *branch_name_column;
282 GtkCellRenderer *branch_active_icon_renderer;
284 self->priv = g_new0 (GitBranchesPanePriv, 1);
285 self->priv->builder = gtk_builder_new ();
286 self->priv->selected_local_branches = g_hash_table_new_full (g_str_hash,
287 g_str_equal,
288 g_free,
289 NULL);
290 self->priv->selected_remote_branches = g_hash_table_new_full (g_str_hash,
291 g_str_equal,
292 g_free,
293 NULL);
296 if (!gtk_builder_add_objects_from_file (self->priv->builder, BUILDER_FILE,
297 objects,
298 &error))
300 g_warning ("Couldn't load builder file: %s", error->message);
301 g_error_free (error);
304 branches_view = GTK_TREE_VIEW (gtk_builder_get_object (self->priv->builder,
305 "branches_view"));
306 branch_selected_column = GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self->priv->builder,
307 "branch_selected_column"));
308 branch_selected_renderer = GTK_CELL_RENDERER (gtk_builder_get_object (self->priv->builder,
309 "branch_selected_renderer"));
310 branch_name_column = GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self->priv->builder,
311 "branch_name_column"));
312 branch_active_icon_renderer = GTK_CELL_RENDERER (gtk_builder_get_object (self->priv->builder,
313 "branch_active_icon_renderer"));
315 /* DND */
316 gtk_tree_view_enable_model_drag_source (branches_view,
317 GDK_BUTTON1_MASK,
318 drag_targets,
319 G_N_ELEMENTS (drag_targets),
320 GDK_ACTION_COPY);
322 g_signal_connect (G_OBJECT (branches_view), "drag-data-get",
323 G_CALLBACK (on_branches_list_view_drag_data_get),
324 self);
326 gtk_tree_view_column_set_cell_data_func (branch_selected_column,
327 branch_selected_renderer,
328 (GtkTreeCellDataFunc) selected_column_data_func,
329 self,
330 NULL);
332 gtk_tree_view_column_set_cell_data_func (branch_name_column,
333 branch_active_icon_renderer,
334 (GtkTreeCellDataFunc) active_icon_data_func,
335 self,
336 NULL);
338 g_signal_connect (G_OBJECT (branch_selected_renderer), "toggled",
339 G_CALLBACK (on_branch_selected_renderer_toggled),
340 self);
343 static void
344 git_branches_pane_finalize (GObject *object)
346 GitBranchesPane *self;
348 self = GIT_BRANCHES_PANE (object);
350 g_object_unref (self->priv->builder);
351 g_hash_table_destroy (self->priv->selected_local_branches);
352 g_hash_table_destroy (self->priv->selected_remote_branches);
353 g_free (self->priv);
355 G_OBJECT_CLASS (git_branches_pane_parent_class)->finalize (object);
358 static GtkWidget *
359 git_branches_pane_get_widget (AnjutaDockPane *pane)
361 GitBranchesPane *self;
363 self = GIT_BRANCHES_PANE (pane);
365 return GTK_WIDGET (gtk_builder_get_object (self->priv->builder,
366 "branches_pane"));
369 static void
370 git_branches_pane_refresh (AnjutaDockPane *pane)
372 /* TODO: Add private function implementation here */
375 static void
376 git_branches_pane_class_init (GitBranchesPaneClass *klass)
378 GObjectClass* object_class = G_OBJECT_CLASS (klass);
379 AnjutaDockPaneClass *pane_class = ANJUTA_DOCK_PANE_CLASS (klass);
381 object_class->finalize = git_branches_pane_finalize;
382 pane_class->get_widget = git_branches_pane_get_widget;
383 pane_class->refresh = git_branches_pane_refresh;
387 AnjutaDockPane *
388 git_branches_pane_new (Git *plugin)
390 GitBranchesPane *self;
392 self = g_object_new (GIT_TYPE_BRANCHES_PANE, "plugin", plugin, NULL);
394 g_signal_connect (G_OBJECT (plugin->local_branch_list_command),
395 "command-started",
396 G_CALLBACK (on_local_branch_list_command_started),
397 self);
399 g_signal_connect (G_OBJECT (plugin->remote_branch_list_command),
400 "command-finished",
401 G_CALLBACK (on_remote_branch_list_command_finished),
402 self);
404 g_signal_connect (G_OBJECT (plugin->local_branch_list_command),
405 "data-arrived",
406 G_CALLBACK (on_local_branch_list_command_data_arrived),
407 self);
409 g_signal_connect (G_OBJECT (plugin->remote_branch_list_command),
410 "data-arrived",
411 G_CALLBACK (on_remote_branch_list_command_data_arrived),
412 self);
414 return ANJUTA_DOCK_PANE (self);
417 GList *
418 git_branches_pane_get_selected_local_branches (GitBranchesPane *self)
420 GList *list;
422 list = NULL;
424 g_hash_table_foreach (self->priv->selected_local_branches,
425 (GHFunc) selected_branches_table_foreach,
426 &list);
428 return list;
431 GList *
432 git_branches_pane_get_selected_remote_branches (GitBranchesPane *self)
434 GList *list;
436 list = NULL;
438 g_hash_table_foreach (self->priv->selected_remote_branches,
439 (GHFunc) selected_branches_table_foreach,
440 &list);
442 return list;
445 gsize
446 git_branches_pane_count_selected_items (GitBranchesPane *self)
448 return (g_hash_table_size (self->priv->selected_local_branches)) +
449 (g_hash_table_size (self->priv->selected_remote_branches));
452 gchar *
453 git_branches_pane_get_selected_branch (GitBranchesPane *self)
455 gchar *selected_branch;
456 GtkTreeView *branches_view;
457 GtkTreeSelection *selection;
458 GtkTreeModel *branches_list_model;
459 GtkTreeIter iter;
461 selected_branch = NULL;
462 branches_view = GTK_TREE_VIEW (gtk_builder_get_object (self->priv->builder,
463 "branches_view"));
464 selection = gtk_tree_view_get_selection (branches_view);
466 if (gtk_tree_selection_count_selected_rows (selection) > 0)
468 gtk_tree_selection_get_selected (selection, &branches_list_model,
469 &iter);
471 gtk_tree_model_get (branches_list_model, &iter, COL_NAME,
472 &selected_branch, -1);
475 return selected_branch;
478 void git_branches_pane_update_ui (GitBranchesPane *self)
480 gint n_selection;
482 /* Enable only actions that make sense with the selection */
483 if (!self->priv->merge_action ||
484 !self->priv->delete_action ||
485 !self->priv->switch_action)
487 Git* plugin =
488 ANJUTA_PLUGIN_GIT (anjuta_dock_pane_get_plugin (ANJUTA_DOCK_PANE (self)));
489 AnjutaCommandBar* bar = anjuta_dock_get_command_bar (ANJUTA_DOCK(plugin->dock));
490 self->priv->merge_action = anjuta_command_bar_get_action (bar,
491 "Branches",
492 "Merge");
493 self->priv->delete_action = anjuta_command_bar_get_action (bar,
494 "Branches",
495 "DeleteBranches");
496 self->priv->switch_action = anjuta_command_bar_get_action (bar,
497 "Branches",
498 "Switch");
500 n_selection = g_hash_table_size (self->priv->selected_local_branches) +
501 g_hash_table_size (self->priv->selected_remote_branches);
502 gtk_action_set_sensitive (self->priv->merge_action, n_selection > 0);
503 gtk_action_set_sensitive (self->priv->delete_action, n_selection > 0);
504 gtk_action_set_sensitive (self->priv->switch_action, n_selection == 1);