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
;
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. */
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
,
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
);
73 on_remote_branch_list_command_finished (AnjutaCommand
*command
,
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
,
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
));
90 on_local_branch_list_command_data_arrived (AnjutaCommand
*command
,
91 GitBranchesPane
*self
)
93 GtkListStore
*branches_list_model
;
94 GList
*current_branch
;
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
,
120 current_branch
= g_list_next (current_branch
);
125 on_remote_branch_list_command_data_arrived (AnjutaCommand
*command
,
126 GitBranchesPane
*self
)
128 GtkListStore
*branches_list_model
;
129 GList
*current_branch
;
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
,
157 current_branch
= g_list_next (current_branch
);
162 selected_column_data_func (GtkTreeViewColumn
*column
, GtkCellRenderer
*renderer
,
163 GtkTreeModel
*model
, GtkTreeIter
*iter
,
164 GitBranchesPane
*self
)
169 gtk_tree_model_get (model
, iter
, COL_SELECTED
, &selected
, COL_ACTIVE
,
172 gtk_cell_renderer_toggle_set_active (GTK_CELL_RENDERER_TOGGLE (renderer
),
174 gtk_cell_renderer_toggle_set_activatable (GTK_CELL_RENDERER_TOGGLE (renderer
),
179 active_icon_data_func (GtkTreeViewColumn
*column
, GtkCellRenderer
*renderer
,
180 GtkTreeModel
*model
, GtkTreeIter
*iter
,
181 GitBranchesPane
*self
)
185 gtk_tree_model_get (model
, iter
, COL_ACTIVE
, &active
, -1);
188 g_object_set (renderer
, "stock-id", GTK_STOCK_APPLY
, NULL
);
190 g_object_set (renderer
, "stock-id", NULL
, NULL
);
194 on_branch_selected_renderer_toggled (GtkCellRendererToggle
*renderer
,
195 gchar
*path
, GitBranchesPane
*self
)
197 GtkTreeModel
*branches_list_model
;
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
,
214 selected
= !selected
;
217 selection_table
= self
->priv
->selected_remote_branches
;
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. */
224 g_hash_table_insert (selection_table
, name
, NULL
);
226 g_hash_table_remove (selection_table
, name
);
228 gtk_list_store_set (GTK_LIST_STORE (branches_list_model
), &iter
, 0, selected
,
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
;
241 GtkTreeModel
*branches_list_model
;
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
,
251 gtk_tree_model_get (branches_list_model
, &iter
, COL_NAME
, &name
, -1);
253 gtk_selection_data_set_text (data
, name
, -1);
260 selected_branches_table_foreach (gchar
*name
, gpointer value
,
263 *list
= g_list_append (*list
, g_strdup (name
));
267 on_branches_view_row_activated (GtkTreeView
*branches_view
, GtkTreePath
*path
,
268 GtkTreeViewColumn
*column
, GitBranchesPane
*self
)
274 GitBranchCheckoutCommand
*checkout_command
;
276 model
= gtk_tree_view_get_model (branches_view
);
277 gtk_tree_model_get_iter (model
, &iter
, path
);
279 gtk_tree_model_get (model
, &iter
, COL_NAME
, &branch
, -1);
281 plugin
= ANJUTA_PLUGIN_GIT (anjuta_dock_pane_get_plugin (ANJUTA_DOCK_PANE (self
)));
282 checkout_command
= git_branch_checkout_command_new (plugin
->project_root_directory
,
285 g_signal_connect (G_OBJECT (checkout_command
), "command-finished",
286 G_CALLBACK (git_pane_report_errors
),
289 g_signal_connect (G_OBJECT (checkout_command
), "command-finished",
290 G_CALLBACK (g_object_unref
),
295 anjuta_command_start (ANJUTA_COMMAND (checkout_command
));
299 on_branches_view_button_press_event (GtkWidget
*branches_view
,
300 GdkEventButton
*event
,
301 GitBranchesPane
*self
)
303 GtkTreeSelection
*selection
;
304 AnjutaPlugin
*plugin
;
308 if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 3)
310 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (branches_view
));
312 if (gtk_tree_selection_count_selected_rows (selection
) > 0)
314 plugin
= anjuta_dock_pane_get_plugin (ANJUTA_DOCK_PANE (self
));
315 ui
= anjuta_shell_get_ui (plugin
->shell
, NULL
);
316 menu
= GTK_MENU (gtk_ui_manager_get_widget (GTK_UI_MANAGER (ui
),
319 gtk_menu_popup (menu
, NULL
, NULL
, NULL
, NULL
, event
->button
,
328 git_branches_pane_init (GitBranchesPane
*self
)
330 gchar
*objects
[] = {"branches_pane",
331 "branches_list_model",
333 GError
*error
= NULL
;
334 GtkTreeView
*branches_view
;
335 GtkTreeViewColumn
*branch_selected_column
;
336 GtkCellRenderer
*branch_selected_renderer
;
337 GtkTreeViewColumn
*branch_name_column
;
338 GtkCellRenderer
*branch_active_icon_renderer
;
340 self
->priv
= g_new0 (GitBranchesPanePriv
, 1);
341 self
->priv
->builder
= gtk_builder_new ();
342 self
->priv
->selected_local_branches
= g_hash_table_new_full (g_str_hash
,
346 self
->priv
->selected_remote_branches
= g_hash_table_new_full (g_str_hash
,
352 if (!gtk_builder_add_objects_from_file (self
->priv
->builder
, BUILDER_FILE
,
356 g_warning ("Couldn't load builder file: %s", error
->message
);
357 g_error_free (error
);
360 branches_view
= GTK_TREE_VIEW (gtk_builder_get_object (self
->priv
->builder
,
362 branch_selected_column
= GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self
->priv
->builder
,
363 "branch_selected_column"));
364 branch_selected_renderer
= GTK_CELL_RENDERER (gtk_builder_get_object (self
->priv
->builder
,
365 "branch_selected_renderer"));
366 branch_name_column
= GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self
->priv
->builder
,
367 "branch_name_column"));
368 branch_active_icon_renderer
= GTK_CELL_RENDERER (gtk_builder_get_object (self
->priv
->builder
,
369 "branch_active_icon_renderer"));
372 gtk_tree_view_enable_model_drag_source (branches_view
,
375 G_N_ELEMENTS (drag_targets
),
378 g_signal_connect (G_OBJECT (branches_view
), "drag-data-get",
379 G_CALLBACK (on_branches_list_view_drag_data_get
),
382 gtk_tree_view_column_set_cell_data_func (branch_selected_column
,
383 branch_selected_renderer
,
384 (GtkTreeCellDataFunc
) selected_column_data_func
,
388 gtk_tree_view_column_set_cell_data_func (branch_name_column
,
389 branch_active_icon_renderer
,
390 (GtkTreeCellDataFunc
) active_icon_data_func
,
394 g_signal_connect (G_OBJECT (branch_selected_renderer
), "toggled",
395 G_CALLBACK (on_branch_selected_renderer_toggled
),
398 /* Switch branches on double-click */
399 g_signal_connect (G_OBJECT (branches_view
), "row-activated",
400 G_CALLBACK (on_branches_view_row_activated
),
404 g_signal_connect (G_OBJECT (branches_view
), "button-press-event",
405 G_CALLBACK (on_branches_view_button_press_event
),
410 git_branches_pane_finalize (GObject
*object
)
412 GitBranchesPane
*self
;
414 self
= GIT_BRANCHES_PANE (object
);
416 g_object_unref (self
->priv
->builder
);
417 g_hash_table_destroy (self
->priv
->selected_local_branches
);
418 g_hash_table_destroy (self
->priv
->selected_remote_branches
);
421 G_OBJECT_CLASS (git_branches_pane_parent_class
)->finalize (object
);
425 git_branches_pane_get_widget (AnjutaDockPane
*pane
)
427 GitBranchesPane
*self
;
429 self
= GIT_BRANCHES_PANE (pane
);
431 return GTK_WIDGET (gtk_builder_get_object (self
->priv
->builder
,
436 git_branches_pane_refresh (AnjutaDockPane
*pane
)
438 /* TODO: Add private function implementation here */
442 git_branches_pane_class_init (GitBranchesPaneClass
*klass
)
444 GObjectClass
* object_class
= G_OBJECT_CLASS (klass
);
445 AnjutaDockPaneClass
*pane_class
= ANJUTA_DOCK_PANE_CLASS (klass
);
447 object_class
->finalize
= git_branches_pane_finalize
;
448 pane_class
->get_widget
= git_branches_pane_get_widget
;
449 pane_class
->refresh
= git_branches_pane_refresh
;
454 git_branches_pane_new (Git
*plugin
)
456 GitBranchesPane
*self
;
458 self
= g_object_new (GIT_TYPE_BRANCHES_PANE
, "plugin", plugin
, NULL
);
460 g_signal_connect (G_OBJECT (plugin
->local_branch_list_command
),
462 G_CALLBACK (on_local_branch_list_command_started
),
465 g_signal_connect (G_OBJECT (plugin
->remote_branch_list_command
),
467 G_CALLBACK (on_remote_branch_list_command_finished
),
470 g_signal_connect (G_OBJECT (plugin
->local_branch_list_command
),
472 G_CALLBACK (on_local_branch_list_command_data_arrived
),
475 g_signal_connect (G_OBJECT (plugin
->remote_branch_list_command
),
477 G_CALLBACK (on_remote_branch_list_command_data_arrived
),
480 return ANJUTA_DOCK_PANE (self
);
484 git_branches_pane_get_selected_local_branches (GitBranchesPane
*self
)
490 g_hash_table_foreach (self
->priv
->selected_local_branches
,
491 (GHFunc
) selected_branches_table_foreach
,
498 git_branches_pane_get_selected_remote_branches (GitBranchesPane
*self
)
504 g_hash_table_foreach (self
->priv
->selected_remote_branches
,
505 (GHFunc
) selected_branches_table_foreach
,
512 git_branches_pane_count_selected_items (GitBranchesPane
*self
)
514 return (g_hash_table_size (self
->priv
->selected_local_branches
)) +
515 (g_hash_table_size (self
->priv
->selected_remote_branches
));
519 git_branches_pane_get_selected_branch (GitBranchesPane
*self
)
521 gchar
*selected_branch
;
522 GtkTreeView
*branches_view
;
523 GtkTreeSelection
*selection
;
524 GtkTreeModel
*branches_list_model
;
527 selected_branch
= NULL
;
528 branches_view
= GTK_TREE_VIEW (gtk_builder_get_object (self
->priv
->builder
,
530 selection
= gtk_tree_view_get_selection (branches_view
);
532 if (gtk_tree_selection_count_selected_rows (selection
) > 0)
534 gtk_tree_selection_get_selected (selection
, &branches_list_model
,
537 gtk_tree_model_get (branches_list_model
, &iter
, COL_NAME
,
538 &selected_branch
, -1);
541 return selected_branch
;
545 git_branches_pane_is_selected_branch_remote (GitBranchesPane
*self
)
548 GtkTreeView
*branches_view
;
549 GtkTreeSelection
*selection
;
550 GtkTreeModel
*branches_list_model
;
554 branches_view
= GTK_TREE_VIEW (gtk_builder_get_object (self
->priv
->builder
,
556 selection
= gtk_tree_view_get_selection (branches_view
);
558 if (gtk_tree_selection_count_selected_rows (selection
) > 0)
560 gtk_tree_selection_get_selected (selection
, &branches_list_model
,
563 gtk_tree_model_get (branches_list_model
, &iter
, COL_REMOTE
,
571 clear_branch_selections (GtkTreeModel
*model
, GtkTreePath
*path
,
572 GtkTreeIter
*iter
, gpointer data
)
574 gtk_list_store_set (GTK_LIST_STORE (model
), iter
, COL_SELECTED
, FALSE
, -1);
580 git_branches_pane_set_select_column_visible (GitBranchesPane
*self
,
583 GtkTreeViewColumn
*branch_selected_column
;
584 GtkTreeModel
*branches_list_model
;
586 branch_selected_column
= GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self
->priv
->builder
,
587 "branch_selected_column"));
589 gtk_tree_view_column_set_visible (branch_selected_column
, visible
);
591 /* Clear branch selections when the column becomes invisible again, because
592 * selections have no meaning once an operation that needs these selections
593 * has either been completed or cancelled */
596 branches_list_model
= GTK_TREE_MODEL (gtk_builder_get_object (self
->priv
->builder
,
597 "branches_list_model"));
599 gtk_tree_model_foreach (branches_list_model
,
600 (GtkTreeModelForeachFunc
) clear_branch_selections
,
603 g_hash_table_remove_all (self
->priv
->selected_local_branches
);
604 g_hash_table_remove_all (self
->priv
->selected_remote_branches
);