1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 * Copyright (C) James Liggett 2010 <jrliggett@cox.net>
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"
30 static GtkTargetEntry drag_targets
[] =
39 struct _GitBranchesPanePriv
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. */
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
,
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
);
77 on_remote_branch_list_command_finished (AnjutaCommand
*command
,
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
,
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
));
94 on_local_branch_list_command_data_arrived (AnjutaCommand
*command
,
95 GitBranchesPane
*self
)
97 GtkListStore
*branches_list_model
;
98 GList
*current_branch
;
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
,
124 current_branch
= g_list_next (current_branch
);
129 on_remote_branch_list_command_data_arrived (AnjutaCommand
*command
,
130 GitBranchesPane
*self
)
132 GtkListStore
*branches_list_model
;
133 GList
*current_branch
;
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
,
161 current_branch
= g_list_next (current_branch
);
166 selected_column_data_func (GtkTreeViewColumn
*column
, GtkCellRenderer
*renderer
,
167 GtkTreeModel
*model
, GtkTreeIter
*iter
,
168 GitBranchesPane
*self
)
173 gtk_tree_model_get (model
, iter
, COL_SELECTED
, &selected
, COL_ACTIVE
,
176 gtk_cell_renderer_toggle_set_active (GTK_CELL_RENDERER_TOGGLE (renderer
),
178 gtk_cell_renderer_toggle_set_activatable (GTK_CELL_RENDERER_TOGGLE (renderer
),
183 active_icon_data_func (GtkTreeViewColumn
*column
, GtkCellRenderer
*renderer
,
184 GtkTreeModel
*model
, GtkTreeIter
*iter
,
185 GitBranchesPane
*self
)
189 gtk_tree_model_get (model
, iter
, COL_ACTIVE
, &active
, -1);
192 g_object_set (renderer
, "stock-id", GTK_STOCK_APPLY
, NULL
);
194 g_object_set (renderer
, "stock-id", "", NULL
);
198 on_branch_selected_renderer_toggled (GtkCellRendererToggle
*renderer
,
199 gchar
*path
, GitBranchesPane
*self
)
201 GtkTreeModel
*branches_list_model
;
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
,
218 selected
= !selected
;
221 selection_table
= self
->priv
->selected_remote_branches
;
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. */
228 g_hash_table_insert (selection_table
, name
, NULL
);
230 g_hash_table_remove (selection_table
, name
);
232 gtk_list_store_set (GTK_LIST_STORE (branches_list_model
), &iter
, 0, selected
,
234 git_branches_pane_update_ui (self
);
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
;
246 GtkTreeModel
*branches_list_model
;
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
,
256 gtk_tree_model_get (branches_list_model
, &iter
, COL_NAME
, &name
, -1);
258 gtk_selection_data_set_text (data
, name
, -1);
265 selected_branches_table_foreach (gchar
*name
, gpointer value
,
268 *list
= g_list_append (*list
, g_strdup (name
));
272 git_branches_pane_init (GitBranchesPane
*self
)
274 gchar
*objects
[] = {"branches_pane",
275 "branches_list_model",
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
,
290 self
->priv
->selected_remote_branches
= g_hash_table_new_full (g_str_hash
,
296 if (!gtk_builder_add_objects_from_file (self
->priv
->builder
, BUILDER_FILE
,
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
,
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"));
316 gtk_tree_view_enable_model_drag_source (branches_view
,
319 G_N_ELEMENTS (drag_targets
),
322 g_signal_connect (G_OBJECT (branches_view
), "drag-data-get",
323 G_CALLBACK (on_branches_list_view_drag_data_get
),
326 gtk_tree_view_column_set_cell_data_func (branch_selected_column
,
327 branch_selected_renderer
,
328 (GtkTreeCellDataFunc
) selected_column_data_func
,
332 gtk_tree_view_column_set_cell_data_func (branch_name_column
,
333 branch_active_icon_renderer
,
334 (GtkTreeCellDataFunc
) active_icon_data_func
,
338 g_signal_connect (G_OBJECT (branch_selected_renderer
), "toggled",
339 G_CALLBACK (on_branch_selected_renderer_toggled
),
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
);
355 G_OBJECT_CLASS (git_branches_pane_parent_class
)->finalize (object
);
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
,
370 git_branches_pane_refresh (AnjutaDockPane
*pane
)
372 /* TODO: Add private function implementation here */
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
;
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
),
396 G_CALLBACK (on_local_branch_list_command_started
),
399 g_signal_connect (G_OBJECT (plugin
->remote_branch_list_command
),
401 G_CALLBACK (on_remote_branch_list_command_finished
),
404 g_signal_connect (G_OBJECT (plugin
->local_branch_list_command
),
406 G_CALLBACK (on_local_branch_list_command_data_arrived
),
409 g_signal_connect (G_OBJECT (plugin
->remote_branch_list_command
),
411 G_CALLBACK (on_remote_branch_list_command_data_arrived
),
414 return ANJUTA_DOCK_PANE (self
);
418 git_branches_pane_get_selected_local_branches (GitBranchesPane
*self
)
424 g_hash_table_foreach (self
->priv
->selected_local_branches
,
425 (GHFunc
) selected_branches_table_foreach
,
432 git_branches_pane_get_selected_remote_branches (GitBranchesPane
*self
)
438 g_hash_table_foreach (self
->priv
->selected_remote_branches
,
439 (GHFunc
) selected_branches_table_foreach
,
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
));
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
;
461 selected_branch
= NULL
;
462 branches_view
= GTK_TREE_VIEW (gtk_builder_get_object (self
->priv
->builder
,
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
,
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
)
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
)
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
,
493 self
->priv
->delete_action
= anjuta_command_bar_get_action (bar
,
496 self
->priv
->switch_action
= anjuta_command_bar_get_action (bar
,
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);