1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 * Copyright (C) James Liggett 2010 <jrliggett@cox.net>
6 * anjuta 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 * anjuta 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-log-pane.h"
33 /* Loading view modes */
36 /* Show the real log viewer */
39 /* Show the loading view */
46 BRANCH_COL_ACTIVE_ICON
,
50 /* DnD source targets */
51 static GtkTargetEntry drag_source_targets
[] =
60 /* DnD target targets */
61 static GtkTargetEntry drag_target_targets
[] =
70 struct _GitLogPanePriv
73 GtkListStore
*log_model
;
74 GtkCellRenderer
*graph_renderer
;
78 /* This table maps branch names and iters in the branch combo model. When
79 * branches get refreshed, use this to make sure that the same branch the
80 * user was looking at stays selected, unless that branch no longer exists.
81 * In that case the new active branch is selected */
82 GHashTable
*branches_table
;
83 gchar
*selected_branch
;
84 gboolean viewing_active_branch
;
85 GtkTreePath
*active_branch_path
;
87 /* Loading spinner data */
88 guint current_spin_count
;
89 guint spin_cycle_steps
;
90 guint spin_cycle_duration
;
92 GtkListStore
*log_loading_model
;
93 GtkTreeIter spinner_iter
;
96 GitBranchListCommand
*branch_list_command
;
97 GitLogMessageCommand
*log_message_command
;
98 GitLogCommand
*log_command
;
101 G_DEFINE_TYPE (GitLogPane
, git_log_pane
, GIT_TYPE_PANE
);
104 on_branch_list_command_started (AnjutaCommand
*command
,
107 GtkComboBox
*branch_combo
;
108 GtkListStore
*log_branch_combo_model
;
110 branch_combo
= GTK_COMBO_BOX (gtk_builder_get_object (self
->priv
->builder
,
112 log_branch_combo_model
= GTK_LIST_STORE (gtk_builder_get_object (self
->priv
->builder
,
113 "log_branch_combo_model"));
115 gtk_combo_box_set_model (branch_combo
, NULL
);
116 gtk_list_store_clear (log_branch_combo_model
);
118 g_hash_table_remove_all (self
->priv
->branches_table
);
122 on_branch_list_command_finished (AnjutaCommand
*command
,
126 GtkComboBox
*branch_combo
;
127 GtkTreeModel
*log_branch_combo_model
;
131 branch_combo
= GTK_COMBO_BOX (gtk_builder_get_object (self
->priv
->builder
,
133 log_branch_combo_model
= GTK_TREE_MODEL (gtk_builder_get_object (self
->priv
->builder
,
134 "log_branch_combo_model"));
136 gtk_combo_box_set_model (branch_combo
, log_branch_combo_model
);
138 /* If the user was viewing the active branch, switch to the newly active
139 * branch if it changes. If another branch was being viewed, stay on that
141 if ((!self
->priv
->viewing_active_branch
) &&
142 (self
->priv
->selected_branch
&&
143 g_hash_table_lookup_extended (self
->priv
->branches_table
,
144 self
->priv
->selected_branch
, NULL
,
147 gtk_tree_model_get_iter (log_branch_combo_model
, &iter
, path
);
148 gtk_combo_box_set_active_iter (branch_combo
, &iter
);
150 else if (self
->priv
->active_branch_path
!= NULL
)
152 gtk_tree_model_get_iter (log_branch_combo_model
, &iter
,
153 self
->priv
->active_branch_path
);
154 gtk_combo_box_set_active_iter (branch_combo
, &iter
);
157 g_clear_object (&self
->priv
->branch_list_command
);
161 on_branch_list_command_data_arrived (AnjutaCommand
*command
,
164 GtkListStore
*log_branch_combo_model
;
165 GList
*current_branch
;
170 log_branch_combo_model
= GTK_LIST_STORE (gtk_builder_get_object (self
->priv
->builder
,
171 "log_branch_combo_model"));
172 current_branch
= git_branch_list_command_get_output (GIT_BRANCH_LIST_COMMAND (command
));
174 while (current_branch
)
176 branch
= current_branch
->data
;
177 name
= git_branch_get_name (branch
);
179 gtk_list_store_append (log_branch_combo_model
, &iter
);
181 if (git_branch_is_active (branch
))
183 gtk_list_store_set (log_branch_combo_model
, &iter
,
184 BRANCH_COL_ACTIVE
, TRUE
,
185 BRANCH_COL_ACTIVE_ICON
, GTK_STOCK_APPLY
,
188 if (self
->priv
->active_branch_path
!= NULL
)
189 gtk_tree_path_free (self
->priv
->active_branch_path
);
191 self
->priv
->active_branch_path
= gtk_tree_model_get_path (GTK_TREE_MODEL (log_branch_combo_model
),
196 gtk_list_store_set (log_branch_combo_model
, &iter
,
197 BRANCH_COL_ACTIVE
, FALSE
,
198 BRANCH_COL_ACTIVE_ICON
, NULL
,
202 gtk_list_store_set (log_branch_combo_model
, &iter
,
203 BRANCH_COL_NAME
, name
,
205 g_hash_table_insert (self
->priv
->branches_table
, g_strdup (name
),
206 gtk_tree_model_get_path (GTK_TREE_MODEL (log_branch_combo_model
),
211 current_branch
= g_list_next (current_branch
);
217 on_spinner_timeout (GitLogPane
*self
)
219 if (self
->priv
->current_spin_count
== self
->priv
->spin_cycle_steps
)
220 self
->priv
->current_spin_count
= 0;
222 self
->priv
->current_spin_count
++;
224 gtk_list_store_set (self
->priv
->log_loading_model
,
225 &(self
->priv
->spinner_iter
),
227 self
->priv
->current_spin_count
,
233 git_log_pane_set_view_mode (GitLogPane
*self
, LogViewMode mode
)
235 GtkNotebook
*loading_notebook
;
237 loading_notebook
= GTK_NOTEBOOK (gtk_builder_get_object (self
->priv
->builder
,
238 "loading_notebook"));
242 case LOG_VIEW_LOADING
:
243 /* Don't create more than one timer */
244 if (self
->priv
->spin_timer_id
<= 0)
246 self
->priv
->spin_timer_id
= g_timeout_add ((guint
) self
->priv
->spin_cycle_duration
/ self
->priv
->spin_cycle_steps
,
247 (GSourceFunc
) on_spinner_timeout
,
252 case LOG_VIEW_NORMAL
:
253 if (self
->priv
->spin_timer_id
> 0)
255 g_source_remove (self
->priv
->spin_timer_id
);
256 self
->priv
->spin_timer_id
= 0;
259 /* Reset the spinner */
260 self
->priv
->current_spin_count
= 0;
262 gtk_list_store_set (self
->priv
->log_loading_model
,
263 &(self
->priv
->spinner_iter
),
264 LOADING_COL_PULSE
, 0,
271 gtk_notebook_set_current_page (loading_notebook
, mode
);
275 on_log_command_finished (AnjutaCommand
*command
, guint return_code
,
278 GtkTreeView
*log_view
;
281 GitRevision
*revision
;
283 /* Show the actual log view */
284 git_log_pane_set_view_mode (self
, LOG_VIEW_NORMAL
);
286 log_view
= GTK_TREE_VIEW (gtk_builder_get_object (self
->priv
->builder
,
289 if (return_code
!= 0)
291 /* Don't report erros in the log view as this is usually no user requested
292 * operation and thus error messages are confusing instead just show an
296 git_pane_report_errors (command
, return_code
,
297 ANJUTA_PLUGIN_GIT (anjuta_dock_pane_get_plugin (ANJUTA_DOCK_PANE (self
))));
302 queue
= git_log_command_get_output_queue (GIT_LOG_COMMAND (command
));
304 while (g_queue_peek_head (queue
))
306 revision
= g_queue_pop_head (queue
);
308 gtk_list_store_append (self
->priv
->log_model
, &iter
);
309 gtk_list_store_set (self
->priv
->log_model
, &iter
, LOG_COL_REVISION
,
312 g_object_unref (revision
);
315 giggle_graph_renderer_validate_model (GIGGLE_GRAPH_RENDERER (self
->priv
->graph_renderer
),
316 GTK_TREE_MODEL (self
->priv
->log_model
),
318 gtk_tree_view_set_model (GTK_TREE_VIEW (log_view
),
319 GTK_TREE_MODEL (self
->priv
->log_model
));
322 g_clear_object (&self
->priv
->log_command
);
326 refresh_log (GitLogPane
*self
)
329 GtkTreeView
*log_view
;
330 GtkTreeViewColumn
*graph_column
;
332 plugin
= ANJUTA_PLUGIN_GIT (anjuta_dock_pane_get_plugin (ANJUTA_DOCK_PANE (self
)));
333 log_view
= GTK_TREE_VIEW (gtk_builder_get_object (self
->priv
->builder
,
335 graph_column
= GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self
->priv
->builder
,
338 /* Unref the previous command if it's still running. */
339 if (self
->priv
->log_command
)
340 g_object_unref (self
->priv
->log_command
);
342 gtk_tree_view_set_model (log_view
, NULL
);
344 /* We don't support filters for now */
345 self
->priv
->log_command
= git_log_command_new (plugin
->project_root_directory
,
346 self
->priv
->selected_branch
,
355 /* Hide the graph column if we're looking at the log of a path. The graph
356 * won't be correct in this case. */
357 if (self
->priv
->path
)
358 gtk_tree_view_column_set_visible (graph_column
, FALSE
);
360 gtk_tree_view_column_set_visible (graph_column
, TRUE
);
362 g_signal_connect_object (G_OBJECT (self
->priv
->log_command
), "command-finished",
363 G_CALLBACK (on_log_command_finished
),
366 gtk_list_store_clear (self
->priv
->log_model
);
368 /* Show the loading spinner */
369 git_log_pane_set_view_mode (self
, LOG_VIEW_LOADING
);
371 anjuta_command_start (ANJUTA_COMMAND (self
->priv
->log_command
));
375 on_ref_command_finished (AnjutaCommand
*command
, guint return_code
,
380 plugin
= ANJUTA_PLUGIN_GIT (anjuta_dock_pane_get_plugin (ANJUTA_DOCK_PANE (self
)));
382 if (self
->priv
->refs
)
383 g_hash_table_unref (self
->priv
->refs
);
385 self
->priv
->refs
= git_ref_command_get_refs (GIT_REF_COMMAND (command
));
387 /* Unref the previous command if it's still running. */
388 if (self
->priv
->branch_list_command
)
389 g_object_unref (self
->priv
->branch_list_command
);
391 /* Refresh the branch display after the refs get updated */
392 self
->priv
->branch_list_command
=
393 git_branch_list_command_new (plugin
->project_root_directory
,
394 GIT_BRANCH_TYPE_ALL
);
396 g_signal_connect_object (G_OBJECT (self
->priv
->branch_list_command
), "command-started",
397 G_CALLBACK (on_branch_list_command_started
),
400 g_signal_connect_object (G_OBJECT (self
->priv
->branch_list_command
), "command-finished",
401 G_CALLBACK (on_branch_list_command_finished
),
404 g_signal_connect_object (G_OBJECT (self
->priv
->branch_list_command
), "data-arrived",
405 G_CALLBACK (on_branch_list_command_data_arrived
),
408 anjuta_command_start (ANJUTA_COMMAND (self
->priv
->branch_list_command
));
412 on_branch_combo_changed (GtkComboBox
*combo_box
, GitLogPane
*self
)
414 GtkTreeModel
*log_branch_combo_model
;
419 log_branch_combo_model
= gtk_combo_box_get_model (combo_box
);
421 if (gtk_combo_box_get_active_iter (combo_box
, &iter
))
423 gtk_tree_model_get (log_branch_combo_model
, &iter
,
424 BRANCH_COL_ACTIVE
, &active
,
425 BRANCH_COL_NAME
, &branch
,
428 self
->priv
->viewing_active_branch
= active
;
430 g_free (self
->priv
->selected_branch
);
431 self
->priv
->selected_branch
= g_strdup (branch
);
435 /* Refresh the log after the branches are refreshed so that everything
436 * happens in a consistent order (refs, branches, log) and that
437 * the log only gets refreshed once. Doing the refresh here also
438 * lets us kill two birds with one stone: we can handle refreshes and
439 * different branch selections all in one place. */
446 author_cell_function (GtkTreeViewColumn
*column
, GtkCellRenderer
*renderer
,
447 GtkTreeModel
*model
, GtkTreeIter
*iter
,
450 GitRevision
*revision
;
453 gtk_tree_model_get (model
, iter
, LOG_COL_REVISION
, &revision
, -1);
454 author
= git_revision_get_author (revision
);
456 g_object_unref (revision
);
458 g_object_set (renderer
, "text", author
, NULL
);
464 date_cell_function (GtkTreeViewColumn
*column
, GtkCellRenderer
*renderer
,
465 GtkTreeModel
*model
, GtkTreeIter
*iter
, gpointer user_data
)
467 GitRevision
*revision
;
470 gtk_tree_model_get (model
, iter
, LOG_COL_REVISION
, &revision
, -1);
471 date
= git_revision_get_formatted_date (revision
);
473 g_object_unref (revision
);
475 g_object_set (renderer
, "text", date
, NULL
);
481 short_log_cell_function (GtkTreeViewColumn
*column
, GtkCellRenderer
*renderer
,
482 GtkTreeModel
*model
, GtkTreeIter
*iter
,
485 GitRevision
*revision
;
488 gtk_tree_model_get (model
, iter
, LOG_COL_REVISION
, &revision
, -1);
489 short_log
= git_revision_get_short_log (revision
);
491 g_object_unref (revision
);
493 g_object_set (renderer
, "text", short_log
, NULL
);
499 ref_icon_cell_function (GtkTreeViewColumn
*column
, GtkCellRenderer
*renderer
,
500 GtkTreeModel
*model
, GtkTreeIter
*iter
,
503 GitRevision
*revision
;
506 gtk_tree_model_get (model
, iter
, LOG_COL_REVISION
, &revision
, -1);
507 sha
= git_revision_get_sha (revision
);
509 g_object_unref (revision
);
511 if (g_hash_table_lookup_extended (self
->priv
->refs
, sha
, NULL
, NULL
))
512 g_object_set (renderer
, "stock-id", GTK_STOCK_INFO
, NULL
);
514 g_object_set (renderer
, "stock-id", NULL
, NULL
);
520 on_log_view_query_tooltip (GtkWidget
*log_view
, gint x
, gint y
,
521 gboolean keyboard_mode
, GtkTooltip
*tooltip
,
525 GtkTreeViewColumn
*ref_icon_column
;
528 GtkTreeViewColumn
*current_column
;
532 GitRevision
*revision
;
536 GString
*tooltip_string
;
542 ref_icon_column
= gtk_tree_view_get_column (GTK_TREE_VIEW (log_view
), 0);
544 gtk_tree_view_convert_widget_to_bin_window_coords (GTK_TREE_VIEW (log_view
),
545 x
, y
, &bin_x
, &bin_y
);
546 if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (log_view
), bin_x
,
547 bin_y
, &path
, ¤t_column
, NULL
,
550 /* We need to be in the ref icon column */
551 if (current_column
== ref_icon_column
)
553 model
= gtk_tree_view_get_model (GTK_TREE_VIEW (log_view
));
554 gtk_tree_model_get_iter (model
, &iter
, path
);
556 gtk_tree_model_get (model
, &iter
, LOG_COL_REVISION
, &revision
, -1);
557 sha
= git_revision_get_sha (revision
);
559 g_object_unref (revision
);
561 ref_list
= g_hash_table_lookup (self
->priv
->refs
, sha
);
566 current_ref
= ref_list
;
567 tooltip_string
= g_string_new ("");
571 ref_name
= git_ref_get_name (GIT_REF (current_ref
->data
));
572 ref_type
= git_ref_get_ref_type (GIT_REF (current_ref
->data
));
574 if (tooltip_string
->len
> 0)
575 g_string_append (tooltip_string
, "\n");
579 case GIT_REF_TYPE_BRANCH
:
580 g_string_append_printf (tooltip_string
,
581 _("<b>Branch:</b> %s"),
584 case GIT_REF_TYPE_TAG
:
585 g_string_append_printf (tooltip_string
,
589 case GIT_REF_TYPE_REMOTE
:
590 g_string_append_printf (tooltip_string
,
591 _("<b>Remote:</b> %s"),
599 current_ref
= g_list_next (current_ref
);
602 gtk_tooltip_set_markup (tooltip
, tooltip_string
->str
);
603 g_string_free (tooltip_string
, TRUE
);
609 gtk_tree_path_free (path
);
616 on_log_message_command_finished (AnjutaCommand
*command
, guint return_code
,
619 GtkWidget
*log_text_view
;
620 GtkTextBuffer
*buffer
;
623 log_text_view
= GTK_WIDGET (gtk_builder_get_object (self
->priv
->builder
,
625 buffer
= gtk_text_view_get_buffer (GTK_TEXT_VIEW (log_text_view
));
626 log_message
= git_log_message_command_get_message (GIT_LOG_MESSAGE_COMMAND (command
));
628 gtk_text_buffer_set_text (buffer
, log_message
, strlen (log_message
));
630 g_free (log_message
);
632 g_clear_object(&self
->priv
->log_message_command
);
636 on_log_view_row_selected (GtkTreeSelection
*selection
,
639 gboolean path_currently_selected
,
644 GitRevision
*revision
;
647 if (!path_currently_selected
)
649 plugin
= ANJUTA_PLUGIN_GIT (anjuta_dock_pane_get_plugin (ANJUTA_DOCK_PANE (self
)));
651 gtk_tree_model_get_iter (model
, &iter
, path
);
652 gtk_tree_model_get (model
, &iter
, LOG_COL_REVISION
, &revision
, -1);
653 sha
= git_revision_get_sha (revision
);
655 /* Unref the previous command if it's still running. */
656 if (self
->priv
->log_message_command
)
657 g_object_unref (self
->priv
->log_message_command
);
659 self
->priv
->log_message_command
=
660 git_log_message_command_new (plugin
->project_root_directory
, sha
);
663 g_object_unref (revision
);
665 g_signal_connect_object (G_OBJECT (self
->priv
->log_message_command
), "command-finished",
666 G_CALLBACK (on_log_message_command_finished
),
669 anjuta_command_start (ANJUTA_COMMAND (self
->priv
->log_message_command
));
676 on_log_view_drag_data_get (GtkWidget
*log_view
,
677 GdkDragContext
*drag_context
,
678 GtkSelectionData
*data
,
679 guint info
, guint time
,
682 GtkTreeSelection
*selection
;
684 GitRevision
*revision
;
687 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (log_view
));
689 if (gtk_tree_selection_count_selected_rows (selection
) > 0)
691 gtk_tree_selection_get_selected (selection
, NULL
, &iter
);
693 gtk_tree_model_get (GTK_TREE_MODEL (self
->priv
->log_model
), &iter
,
696 sha
= git_revision_get_sha (revision
);
698 gtk_selection_data_set_text (data
, sha
, -1);
700 g_object_unref (revision
);
706 on_log_pane_drag_data_received (GtkWidget
*widget
,
707 GdkDragContext
*context
, gint x
, gint y
,
708 GtkSelectionData
*data
, guint target_type
,
709 guint time
, GitLogPane
*self
)
712 AnjutaEntry
*path_entry
;
719 plugin
= ANJUTA_PLUGIN_GIT (anjuta_dock_pane_get_plugin (ANJUTA_DOCK_PANE (self
)));
720 path_entry
= ANJUTA_ENTRY (gtk_builder_get_object (self
->priv
->builder
,
724 if ((data
!= NULL
) &&
725 (gtk_selection_data_get_length (data
) >= 0))
727 if (target_type
== 0)
729 uri_list
= gtk_selection_data_get_uris (data
);
732 parent_file
= g_file_new_for_path (plugin
->project_root_directory
);
734 /* Take only the first file */
735 file
= g_file_new_for_uri (uri_list
[0]);
739 path
= g_file_get_relative_path (parent_file
, file
);
741 g_object_unref (parent_file
);
744 path
= g_file_get_path (file
);
748 anjuta_entry_set_text (path_entry
, path
);
750 g_free (self
->priv
->path
);
751 self
->priv
->path
= g_strdup (path
);
760 g_object_unref (file
);
761 g_strfreev (uri_list
);
765 /* Do not delete source data */
766 gtk_drag_finish (context
, success
, FALSE
, time
);
770 on_log_pane_drag_drop (GtkWidget
*widget
, GdkDragContext
*context
,
771 gint x
, gint y
, guint time
,
776 target_type
= gtk_drag_dest_find_target (widget
, context
, NULL
);
778 if (target_type
!= GDK_NONE
)
779 gtk_drag_get_data (widget
, context
, target_type
, time
);
781 gtk_drag_finish (context
, FALSE
, FALSE
, time
);
787 on_path_entry_icon_release (GtkEntry
*entry
,
788 GtkEntryIconPosition position
,
792 if (position
== GTK_ENTRY_ICON_SECONDARY
)
794 if (self
->priv
->path
)
796 g_free (self
->priv
->path
);
797 self
->priv
->path
= NULL
;
805 on_log_view_button_press_event (GtkWidget
*log_view
, GdkEventButton
*event
,
809 GtkTreeSelection
*selection
;
810 AnjutaPlugin
*plugin
;
813 if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 3)
815 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (log_view
));
817 if (gtk_tree_selection_count_selected_rows (selection
) > 0)
819 plugin
= anjuta_dock_pane_get_plugin (ANJUTA_DOCK_PANE (self
));
820 ui
= anjuta_shell_get_ui (plugin
->shell
, NULL
);
822 menu
= GTK_MENU (gtk_ui_manager_get_widget (GTK_UI_MANAGER (ui
),
825 gtk_menu_popup (menu
, NULL
, NULL
, NULL
, NULL
, event
->button
,
834 git_log_pane_init (GitLogPane
*self
)
836 gchar
*objects
[] = {"log_pane",
837 "log_branch_combo_model",
841 GError
*error
= NULL
;
843 GtkWidget
*path_entry
;
844 GtkTreeView
*log_view
;
845 GtkTreeViewColumn
*ref_icon_column
;
846 GtkTreeViewColumn
*graph_column
;
847 GtkTreeViewColumn
*short_log_column
;
848 GtkTreeViewColumn
*author_column
;
849 GtkTreeViewColumn
*date_column
;
850 GtkCellRenderer
*ref_icon_renderer
;
851 GtkCellRenderer
*short_log_renderer
;
852 GtkCellRenderer
*author_renderer
;
853 GtkCellRenderer
*date_renderer
;
854 GtkTreeViewColumn
*loading_spinner_column
;
855 GtkCellRenderer
*loading_spinner_renderer
;
856 GtkCellRenderer
*loading_indicator_renderer
;
857 GtkComboBox
*branch_combo
;
858 GtkTreeSelection
*selection
;
860 self
->priv
= g_new0 (GitLogPanePriv
, 1);
861 self
->priv
->builder
= gtk_builder_new ();
863 if (!gtk_builder_add_objects_from_file (self
->priv
->builder
, BUILDER_FILE
,
867 g_warning ("Couldn't load builder file: %s", error
->message
);
868 g_error_free (error
);
871 log_pane
= GTK_WIDGET (gtk_builder_get_object (self
->priv
->builder
,
873 path_entry
= GTK_WIDGET (gtk_builder_get_object (self
->priv
->builder
,
875 log_view
= GTK_TREE_VIEW (gtk_builder_get_object (self
->priv
->builder
,
877 ref_icon_column
= GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self
->priv
->builder
,
879 graph_column
= GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self
->priv
->builder
,
881 short_log_column
= GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self
->priv
->builder
,
882 "short_log_column"));
883 author_column
= GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self
->priv
->builder
,
885 date_column
= GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self
->priv
->builder
,
887 ref_icon_renderer
= GTK_CELL_RENDERER (gtk_builder_get_object (self
->priv
->builder
,
888 "ref_icon_renderer"));
889 author_renderer
= GTK_CELL_RENDERER (gtk_builder_get_object (self
->priv
->builder
,
891 date_renderer
= GTK_CELL_RENDERER (gtk_builder_get_object (self
->priv
->builder
,
893 branch_combo
= GTK_COMBO_BOX (gtk_builder_get_object (self
->priv
->builder
,
895 loading_spinner_column
= GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self
->priv
->builder
,
896 "loading_spinner_column"));
897 selection
= gtk_tree_view_get_selection (log_view
);
900 g_signal_connect (G_OBJECT (path_entry
), "icon-release",
901 G_CALLBACK (on_path_entry_icon_release
),
904 /* Set up the log model */
905 self
->priv
->log_model
= gtk_list_store_new (1, GIT_TYPE_REVISION
);
907 /* Ref icon column */
908 gtk_tree_view_column_set_cell_data_func (ref_icon_column
, ref_icon_renderer
,
909 (GtkTreeCellDataFunc
) ref_icon_cell_function
,
914 self
->priv
->graph_renderer
= giggle_graph_renderer_new ();
916 gtk_tree_view_column_pack_start (graph_column
, self
->priv
->graph_renderer
,
918 gtk_tree_view_column_add_attribute (graph_column
, self
->priv
->graph_renderer
,
921 /* Short log column. We have to create this render ouselves becuause Glade
922 * doesn't seem to give us to option to pack it with expand */
923 short_log_renderer
= gtk_cell_renderer_text_new ();
924 g_object_set (G_OBJECT (short_log_renderer
), "ellipsize",
925 PANGO_ELLIPSIZE_END
, NULL
);
926 gtk_tree_view_column_pack_start (short_log_column
, short_log_renderer
,
928 gtk_tree_view_column_set_cell_data_func (short_log_column
, short_log_renderer
,
929 (GtkTreeCellDataFunc
) short_log_cell_function
,
933 gtk_tree_view_column_set_cell_data_func (author_column
, author_renderer
,
934 (GtkTreeCellDataFunc
) author_cell_function
,
938 gtk_tree_view_column_set_cell_data_func (date_column
, date_renderer
,
939 (GtkTreeCellDataFunc
) date_cell_function
,
942 gtk_tree_view_set_model (log_view
, GTK_TREE_MODEL (self
->priv
->log_model
));
944 /* Ref icon tooltip */
945 g_signal_connect (G_OBJECT (log_view
), "query-tooltip",
946 G_CALLBACK (on_log_view_query_tooltip
),
949 /* Loading indicator. The loading indicator is a second tree view display
950 * that looks just like the real log display, except that it displays a
951 * spinner renderer and the text "Loading..." in the Short Log column. */
952 self
->priv
->log_loading_model
= GTK_LIST_STORE (gtk_builder_get_object (self
->priv
->builder
,
953 "log_loading_model"));
954 loading_spinner_renderer
= gtk_cell_renderer_spinner_new ();
955 loading_indicator_renderer
= gtk_cell_renderer_text_new ();
957 g_object_set (G_OBJECT (loading_spinner_renderer
), "active", TRUE
, NULL
);
959 gtk_tree_view_column_pack_start (loading_spinner_column
,
960 loading_spinner_renderer
, FALSE
);
961 gtk_tree_view_column_pack_start (loading_spinner_column
,
962 loading_indicator_renderer
, TRUE
);
963 gtk_tree_view_column_add_attribute (loading_spinner_column
,
964 loading_spinner_renderer
,
965 "pulse", LOADING_COL_PULSE
);
966 gtk_tree_view_column_add_attribute (loading_spinner_column
,
967 loading_indicator_renderer
,
968 "text", LOADING_COL_INDICATOR
);
971 gtk_tree_view_enable_model_drag_source (log_view
,
974 G_N_ELEMENTS (drag_source_targets
),
977 g_signal_connect (G_OBJECT (log_view
), "drag-data-get",
978 G_CALLBACK (on_log_view_drag_data_get
),
981 /* DnD target. Use this as a means of selecting a file to view the
982 * log of. Files or folders would normally be dragged in from the file
983 * manager, but they can come from any source that supports URI's. */
984 gtk_drag_dest_set (log_pane
,
985 GTK_DEST_DEFAULT_MOTION
| GTK_DEST_DEFAULT_HIGHLIGHT
,
987 G_N_ELEMENTS (drag_target_targets
),
988 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
990 g_signal_connect (G_OBJECT (log_pane
), "drag-data-received",
991 G_CALLBACK (on_log_pane_drag_data_received
),
994 g_signal_connect (G_OBJECT (log_pane
), "drag-drop",
995 G_CALLBACK (on_log_pane_drag_drop
),
999 g_signal_connect (G_OBJECT (log_view
), "button-press-event",
1000 G_CALLBACK (on_log_view_button_press_event
),
1003 /* The loading view always has one row. Cache a copy of its iter for easy
1005 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self
->priv
->log_loading_model
),
1006 &(self
->priv
->spinner_iter
));
1008 /* FIXME: GtkSpinner doesn't have those anymore */
1009 self
->priv
->spin_cycle_duration
= 1000;
1010 self
->priv
->spin_cycle_steps
= 12;
1012 g_object_set (G_OBJECT (loading_spinner_renderer
), "active", TRUE
, NULL
);
1014 /* Log message display */
1015 gtk_tree_selection_set_select_function (selection
,
1016 (GtkTreeSelectionFunc
) on_log_view_row_selected
,
1019 /* Branch handling */
1020 self
->priv
->branches_table
= g_hash_table_new_full (g_str_hash
, g_str_equal
,
1022 (GDestroyNotify
) gtk_tree_path_free
);
1024 g_signal_connect (G_OBJECT (branch_combo
), "changed",
1025 G_CALLBACK (on_branch_combo_changed
),
1031 git_log_pane_finalize (GObject
*object
)
1036 self
= GIT_LOG_PANE (object
);
1038 /* Disconnect signal handler from ref_command. */
1039 plugin
= ANJUTA_PLUGIN_GIT (anjuta_dock_pane_get_plugin (ANJUTA_DOCK_PANE (self
)));
1040 g_signal_handlers_disconnect_by_func (G_OBJECT (plugin
->ref_command
),
1041 on_ref_command_finished
, self
);
1043 g_clear_object (&self
->priv
->branch_list_command
);
1044 g_clear_object (&self
->priv
->log_message_command
);
1045 g_clear_object (&self
->priv
->log_command
);
1047 /* Remove spin timer source. */
1048 if (self
->priv
->spin_timer_id
> 0)
1049 g_source_remove (self
->priv
->spin_timer_id
);
1051 g_object_unref (self
->priv
->builder
);
1052 g_object_unref (self
->priv
->log_model
);
1053 g_free (self
->priv
->path
);
1054 g_hash_table_destroy (self
->priv
->branches_table
);
1056 if (self
->priv
->refs
)
1057 g_hash_table_unref (self
->priv
->refs
);
1059 g_free (self
->priv
->selected_branch
);
1061 if (self
->priv
->active_branch_path
!= NULL
)
1062 gtk_tree_path_free (self
->priv
->active_branch_path
);
1064 g_free (self
->priv
);
1066 G_OBJECT_CLASS (git_log_pane_parent_class
)->finalize (object
);
1070 git_log_pane_get_widget (AnjutaDockPane
*pane
)
1074 self
= GIT_LOG_PANE (pane
);
1076 return GTK_WIDGET (gtk_builder_get_object (self
->priv
->builder
,
1081 git_log_pane_class_init (GitLogPaneClass
*klass
)
1083 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
1084 AnjutaDockPaneClass
*pane_class
= ANJUTA_DOCK_PANE_CLASS (klass
);
1086 object_class
->finalize
= git_log_pane_finalize
;
1087 pane_class
->get_widget
= git_log_pane_get_widget
;
1088 pane_class
->refresh
= NULL
;
1093 git_log_pane_new (Git
*plugin
)
1097 self
= g_object_new (GIT_TYPE_LOG_PANE
, "plugin", plugin
, NULL
);
1099 g_signal_connect_object (G_OBJECT (plugin
->ref_command
), "command-finished",
1100 G_CALLBACK (on_ref_command_finished
),
1103 return ANJUTA_DOCK_PANE (self
);
1107 git_log_pane_set_working_directory (GitLogPane
*self
,
1108 const gchar
*working_directory
)
1110 /* TODO: Add public function implementation here */
1114 git_log_pane_get_selected_revision (GitLogPane
*self
)
1116 GtkTreeView
*log_view
;
1117 GtkTreeSelection
*selection
;
1118 GitRevision
*revision
;
1121 log_view
= GTK_TREE_VIEW (gtk_builder_get_object (self
->priv
->builder
,
1123 selection
= gtk_tree_view_get_selection (log_view
);
1126 if (gtk_tree_selection_get_selected (selection
, NULL
, &iter
))
1128 gtk_tree_model_get (GTK_TREE_MODEL (self
->priv
->log_model
), &iter
,
1129 LOG_COL_REVISION
, &revision
, -1);