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 GtkTreeIter active_branch_iter
;
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 G_DEFINE_TYPE (GitLogPane
, git_log_pane
, GIT_TYPE_PANE
);
99 on_branch_list_command_started (AnjutaCommand
*command
,
102 GtkComboBox
*branch_combo
;
103 GtkListStore
*log_branch_combo_model
;
105 branch_combo
= GTK_COMBO_BOX (gtk_builder_get_object (self
->priv
->builder
,
107 log_branch_combo_model
= GTK_LIST_STORE (gtk_builder_get_object (self
->priv
->builder
,
108 "log_branch_combo_model"));
110 gtk_combo_box_set_model (branch_combo
, NULL
);
111 gtk_list_store_clear (log_branch_combo_model
);
113 g_hash_table_remove_all (self
->priv
->branches_table
);
117 on_branch_list_command_finished (AnjutaCommand
*command
,
121 GtkComboBox
*branch_combo
;
122 GtkTreeModel
*log_branch_combo_model
;
125 branch_combo
= GTK_COMBO_BOX (gtk_builder_get_object (self
->priv
->builder
,
127 log_branch_combo_model
= GTK_TREE_MODEL (gtk_builder_get_object (self
->priv
->builder
,
128 "log_branch_combo_model"));
130 gtk_combo_box_set_model (branch_combo
, log_branch_combo_model
);
132 /* If the user was viewing the active branch, switch to the newly active
133 * branch if it changes. If another branch was being viewed, stay on that
135 if ((!self
->priv
->viewing_active_branch
) &&
136 (self
->priv
->selected_branch
&&
137 g_hash_table_lookup_extended (self
->priv
->branches_table
,
138 self
->priv
->selected_branch
, NULL
,
141 gtk_combo_box_set_active_iter (branch_combo
, iter
);
145 gtk_combo_box_set_active_iter (branch_combo
,
146 &(self
->priv
->active_branch_iter
));
152 on_branch_list_command_data_arrived (AnjutaCommand
*command
,
155 GtkListStore
*log_branch_combo_model
;
156 AnjutaPlugin
*plugin
;
157 GList
*current_branch
;
162 log_branch_combo_model
= GTK_LIST_STORE (gtk_builder_get_object (self
->priv
->builder
,
163 "log_branch_combo_model"));
164 plugin
= anjuta_dock_pane_get_plugin (ANJUTA_DOCK_PANE (self
));
165 current_branch
= git_branch_list_command_get_output (GIT_BRANCH_LIST_COMMAND (command
));
167 while (current_branch
)
169 branch
= current_branch
->data
;
170 name
= git_branch_get_name (branch
);
172 gtk_list_store_append (log_branch_combo_model
, &iter
);
174 if (git_branch_is_active (branch
))
176 gtk_list_store_set (log_branch_combo_model
, &iter
,
177 BRANCH_COL_ACTIVE
, TRUE
,
178 BRANCH_COL_ACTIVE_ICON
, GTK_STOCK_APPLY
,
181 self
->priv
->active_branch_iter
= iter
;
185 gtk_list_store_set (log_branch_combo_model
, &iter
,
186 BRANCH_COL_ACTIVE
, FALSE
,
187 BRANCH_COL_ACTIVE_ICON
, NULL
,
191 gtk_list_store_set (log_branch_combo_model
, &iter
,
192 BRANCH_COL_NAME
, name
,
194 g_hash_table_insert (self
->priv
->branches_table
, g_strdup (name
),
195 g_memdup (&iter
, sizeof (GtkTreeIter
)));
199 current_branch
= g_list_next (current_branch
);
205 on_spinner_timeout (GitLogPane
*self
)
207 if (self
->priv
->current_spin_count
== self
->priv
->spin_cycle_steps
)
208 self
->priv
->current_spin_count
= 0;
210 self
->priv
->current_spin_count
++;
212 gtk_list_store_set (self
->priv
->log_loading_model
,
213 &(self
->priv
->spinner_iter
),
215 self
->priv
->current_spin_count
,
221 git_log_pane_set_view_mode (GitLogPane
*self
, LogViewMode mode
)
223 GtkNotebook
*loading_notebook
;
225 loading_notebook
= GTK_NOTEBOOK (gtk_builder_get_object (self
->priv
->builder
,
226 "loading_notebook"));
230 case LOG_VIEW_LOADING
:
231 /* Don't create more than one timer */
232 if (self
->priv
->spin_timer_id
<= 0)
234 self
->priv
->spin_timer_id
= g_timeout_add ((guint
) self
->priv
->spin_cycle_duration
/ self
->priv
->spin_cycle_steps
,
235 (GSourceFunc
) on_spinner_timeout
,
240 case LOG_VIEW_NORMAL
:
241 if (self
->priv
->spin_timer_id
> 0)
243 g_source_remove (self
->priv
->spin_timer_id
);
244 self
->priv
->spin_timer_id
= 0;
247 /* Reset the spinner */
248 self
->priv
->current_spin_count
= 0;
250 gtk_list_store_set (self
->priv
->log_loading_model
,
251 &(self
->priv
->spinner_iter
),
252 LOADING_COL_PULSE
, 0,
259 gtk_notebook_set_current_page (loading_notebook
, mode
);
263 on_log_command_finished (AnjutaCommand
*command
, guint return_code
,
266 GtkTreeView
*log_view
;
269 GitRevision
*revision
;
271 /* Show the actual log view */
272 git_log_pane_set_view_mode (self
, LOG_VIEW_NORMAL
);
274 if (return_code
!= 0)
276 git_pane_report_errors (command
, return_code
,
277 ANJUTA_PLUGIN_GIT (anjuta_dock_pane_get_plugin (ANJUTA_DOCK_PANE (self
))));
278 g_object_unref (command
);
283 log_view
= GTK_TREE_VIEW (gtk_builder_get_object (self
->priv
->builder
,
286 g_object_ref (self
->priv
->log_model
);
287 gtk_tree_view_set_model (GTK_TREE_VIEW (log_view
), NULL
);
289 queue
= git_log_command_get_output_queue (GIT_LOG_COMMAND (command
));
291 while (g_queue_peek_head (queue
))
293 revision
= g_queue_pop_head (queue
);
295 gtk_list_store_append (self
->priv
->log_model
, &iter
);
296 gtk_list_store_set (self
->priv
->log_model
, &iter
, LOG_COL_REVISION
,
299 g_object_unref (revision
);
302 giggle_graph_renderer_validate_model (GIGGLE_GRAPH_RENDERER (self
->priv
->graph_renderer
),
303 GTK_TREE_MODEL (self
->priv
->log_model
),
305 gtk_tree_view_set_model (GTK_TREE_VIEW (log_view
),
306 GTK_TREE_MODEL (self
->priv
->log_model
));
307 g_object_unref (self
->priv
->log_model
);
309 g_object_unref (command
);
313 refresh_log (GitLogPane
*self
)
316 GtkTreeViewColumn
*graph_column
;
317 GitLogCommand
*log_command
;
319 plugin
= ANJUTA_PLUGIN_GIT (anjuta_dock_pane_get_plugin (ANJUTA_DOCK_PANE (self
)));
320 graph_column
= GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self
->priv
->builder
,
323 /* We don't support filters for now */
324 log_command
= git_log_command_new (plugin
->project_root_directory
,
325 self
->priv
->selected_branch
,
334 /* Hide the graph column if we're looking at the log of a path. The graph
335 * won't be correct in this case. */
336 if (self
->priv
->path
)
337 gtk_tree_view_column_set_visible (graph_column
, FALSE
);
339 gtk_tree_view_column_set_visible (graph_column
, TRUE
);
341 g_signal_connect (G_OBJECT (log_command
), "command-finished",
342 G_CALLBACK (on_log_command_finished
),
345 gtk_list_store_clear (self
->priv
->log_model
);
347 /* Show the loading spinner */
348 git_log_pane_set_view_mode (self
, LOG_VIEW_LOADING
);
350 anjuta_command_start (ANJUTA_COMMAND (log_command
));
354 on_ref_command_finished (AnjutaCommand
*command
, guint return_code
,
358 GitBranchListCommand
*branch_list_command
;
360 plugin
= ANJUTA_PLUGIN_GIT (anjuta_dock_pane_get_plugin (ANJUTA_DOCK_PANE (self
)));
362 if (self
->priv
->refs
)
363 g_hash_table_unref (self
->priv
->refs
);
365 self
->priv
->refs
= git_ref_command_get_refs (GIT_REF_COMMAND (command
));
367 /* Refresh the branch display after the refs get updated */
368 branch_list_command
= git_branch_list_command_new (plugin
->project_root_directory
,
369 GIT_BRANCH_TYPE_ALL
);
371 g_signal_connect (G_OBJECT (branch_list_command
), "command-started",
372 G_CALLBACK (on_branch_list_command_started
),
375 g_signal_connect (G_OBJECT (branch_list_command
), "command-finished",
376 G_CALLBACK (on_branch_list_command_finished
),
379 g_signal_connect (G_OBJECT (branch_list_command
), "command-finished",
380 G_CALLBACK (g_object_unref
),
383 g_signal_connect (G_OBJECT (branch_list_command
), "data-arrived",
384 G_CALLBACK (on_branch_list_command_data_arrived
),
387 anjuta_command_start (ANJUTA_COMMAND (branch_list_command
));
391 on_branch_combo_changed (GtkComboBox
*combo_box
, GitLogPane
*self
)
393 GtkTreeModel
*log_branch_combo_model
;
398 log_branch_combo_model
= gtk_combo_box_get_model (combo_box
);
400 if (gtk_combo_box_get_active_iter (combo_box
, &iter
))
402 gtk_tree_model_get (log_branch_combo_model
, &iter
,
403 BRANCH_COL_ACTIVE
, &active
,
404 BRANCH_COL_NAME
, &branch
,
407 self
->priv
->viewing_active_branch
= active
;
409 g_free (self
->priv
->selected_branch
);
410 self
->priv
->selected_branch
= g_strdup (branch
);
414 /* Refresh the log after the branches are refreshed so that everything
415 * happens in a consistent order (refs, branches, log) and that
416 * the log only gets refreshed once. Doing the refresh here also
417 * lets us kill two birds with one stone: we can handle refreshes and
418 * different branch selections all in one place. */
425 author_cell_function (GtkTreeViewColumn
*column
, GtkCellRenderer
*renderer
,
426 GtkTreeModel
*model
, GtkTreeIter
*iter
,
429 GitRevision
*revision
;
432 gtk_tree_model_get (model
, iter
, LOG_COL_REVISION
, &revision
, -1);
433 author
= git_revision_get_author (revision
);
435 g_object_unref (revision
);
437 g_object_set (renderer
, "text", author
, NULL
);
443 date_cell_function (GtkTreeViewColumn
*column
, GtkCellRenderer
*renderer
,
444 GtkTreeModel
*model
, GtkTreeIter
*iter
, gpointer user_data
)
446 GitRevision
*revision
;
449 gtk_tree_model_get (model
, iter
, LOG_COL_REVISION
, &revision
, -1);
450 date
= git_revision_get_formatted_date (revision
);
452 g_object_unref (revision
);
454 g_object_set (renderer
, "text", date
, NULL
);
460 short_log_cell_function (GtkTreeViewColumn
*column
, GtkCellRenderer
*renderer
,
461 GtkTreeModel
*model
, GtkTreeIter
*iter
,
464 GitRevision
*revision
;
467 gtk_tree_model_get (model
, iter
, LOG_COL_REVISION
, &revision
, -1);
468 short_log
= git_revision_get_short_log (revision
);
470 g_object_unref (revision
);
472 g_object_set (renderer
, "text", short_log
, NULL
);
478 ref_icon_cell_function (GtkTreeViewColumn
*column
, GtkCellRenderer
*renderer
,
479 GtkTreeModel
*model
, GtkTreeIter
*iter
,
482 GitRevision
*revision
;
485 gtk_tree_model_get (model
, iter
, LOG_COL_REVISION
, &revision
, -1);
486 sha
= git_revision_get_sha (revision
);
488 g_object_unref (revision
);
490 if (g_hash_table_lookup_extended (self
->priv
->refs
, sha
, NULL
, NULL
))
491 g_object_set (renderer
, "stock-id", GTK_STOCK_INFO
, NULL
);
493 g_object_set (renderer
, "stock-id", "", NULL
);
499 on_log_view_query_tooltip (GtkWidget
*log_view
, gint x
, gint y
,
500 gboolean keyboard_mode
, GtkTooltip
*tooltip
,
504 GtkTreeViewColumn
*ref_icon_column
;
507 GtkTreeViewColumn
*current_column
;
511 GitRevision
*revision
;
515 GString
*tooltip_string
;
521 ref_icon_column
= gtk_tree_view_get_column (GTK_TREE_VIEW (log_view
), 0);
523 gtk_tree_view_convert_widget_to_bin_window_coords (GTK_TREE_VIEW (log_view
),
524 x
, y
, &bin_x
, &bin_y
);
525 if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (log_view
), bin_x
,
526 bin_y
, &path
, ¤t_column
, NULL
,
529 /* We need to be in the ref icon column */
530 if (current_column
== ref_icon_column
)
532 model
= gtk_tree_view_get_model (GTK_TREE_VIEW (log_view
));
533 gtk_tree_model_get_iter (model
, &iter
, path
);
535 gtk_tree_model_get (model
, &iter
, LOG_COL_REVISION
, &revision
, -1);
536 sha
= git_revision_get_sha (revision
);
538 g_object_unref (revision
);
540 ref_list
= g_hash_table_lookup (self
->priv
->refs
, sha
);
545 current_ref
= ref_list
;
546 tooltip_string
= g_string_new ("");
550 ref_name
= git_ref_get_name (GIT_REF (current_ref
->data
));
551 ref_type
= git_ref_get_ref_type (GIT_REF (current_ref
->data
));
553 if (tooltip_string
->len
> 0)
554 g_string_append (tooltip_string
, "\n");
558 case GIT_REF_TYPE_BRANCH
:
559 g_string_append_printf (tooltip_string
,
560 _("<b>Branch:</b> %s"),
563 case GIT_REF_TYPE_TAG
:
564 g_string_append_printf (tooltip_string
,
568 case GIT_REF_TYPE_REMOTE
:
569 g_string_append_printf (tooltip_string
,
570 _("<b>Remote:</b> %s"),
578 current_ref
= g_list_next (current_ref
);
581 gtk_tooltip_set_markup (tooltip
, tooltip_string
->str
);
582 g_string_free (tooltip_string
, TRUE
);
588 gtk_tree_path_free (path
);
595 on_log_message_command_finished (AnjutaCommand
*command
, guint return_code
,
598 GtkWidget
*log_text_view
;
599 GtkTextBuffer
*buffer
;
602 log_text_view
= GTK_WIDGET (gtk_builder_get_object (self
->priv
->builder
,
604 buffer
= gtk_text_view_get_buffer (GTK_TEXT_VIEW (log_text_view
));
605 log_message
= git_log_message_command_get_message (GIT_LOG_MESSAGE_COMMAND (command
));
607 gtk_text_buffer_set_text (buffer
, log_message
, strlen (log_message
));
609 g_free (log_message
);
610 g_object_unref (command
);
614 on_log_view_row_selected (GtkTreeSelection
*selection
,
617 gboolean path_currently_selected
,
622 GitRevision
*revision
;
624 GitLogMessageCommand
*log_message_command
;
626 if (!path_currently_selected
)
628 plugin
= ANJUTA_PLUGIN_GIT (anjuta_dock_pane_get_plugin (ANJUTA_DOCK_PANE (self
)));
630 gtk_tree_model_get_iter (model
, &iter
, path
);
631 gtk_tree_model_get (model
, &iter
, LOG_COL_REVISION
, &revision
, -1);
632 sha
= git_revision_get_sha (revision
);
634 log_message_command
= git_log_message_command_new (plugin
->project_root_directory
,
638 g_object_unref (revision
);
640 g_signal_connect (G_OBJECT (log_message_command
), "command-finished",
641 G_CALLBACK (on_log_message_command_finished
),
644 anjuta_command_start (ANJUTA_COMMAND (log_message_command
));
651 on_log_view_drag_data_get (GtkWidget
*log_view
,
652 GdkDragContext
*drag_context
,
653 GtkSelectionData
*data
,
654 guint info
, guint time
,
657 GtkTreeSelection
*selection
;
659 GitRevision
*revision
;
662 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (log_view
));
664 if (gtk_tree_selection_count_selected_rows (selection
) > 0)
666 gtk_tree_selection_get_selected (selection
, NULL
, &iter
);
668 gtk_tree_model_get (GTK_TREE_MODEL (self
->priv
->log_model
), &iter
,
671 sha
= git_revision_get_sha (revision
);
673 gtk_selection_data_set_text (data
, sha
, -1);
675 g_object_unref (revision
);
681 on_log_pane_drag_data_received (GtkWidget
*widget
,
682 GdkDragContext
*context
, gint x
, gint y
,
683 GtkSelectionData
*data
, guint target_type
,
684 guint time
, GitLogPane
*self
)
687 AnjutaEntry
*path_entry
;
694 plugin
= ANJUTA_PLUGIN_GIT (anjuta_dock_pane_get_plugin (ANJUTA_DOCK_PANE (self
)));
695 path_entry
= ANJUTA_ENTRY (gtk_builder_get_object (self
->priv
->builder
,
699 if ((data
!= NULL
) &&
700 (gtk_selection_data_get_length (data
) >= 0))
702 if (target_type
== 0)
704 uri_list
= gtk_selection_data_get_uris (data
);
707 parent_file
= g_file_new_for_path (plugin
->project_root_directory
);
709 /* Take only the first file */
710 file
= g_file_new_for_uri (uri_list
[0]);
714 path
= g_file_get_relative_path (parent_file
, file
);
716 g_object_unref (parent_file
);
719 path
= g_file_get_path (file
);
723 anjuta_entry_set_text (path_entry
, path
);
725 g_free (self
->priv
->path
);
726 self
->priv
->path
= g_strdup (path
);
735 g_object_unref (file
);
736 g_strfreev (uri_list
);
740 /* Do not delete source data */
741 gtk_drag_finish (context
, success
, FALSE
, time
);
745 on_log_pane_drag_drop (GtkWidget
*widget
, GdkDragContext
*context
,
746 gint x
, gint y
, guint time
,
751 target_type
= gtk_drag_dest_find_target (widget
, context
, NULL
);
753 if (target_type
!= GDK_NONE
)
754 gtk_drag_get_data (widget
, context
, target_type
, time
);
756 gtk_drag_finish (context
, FALSE
, FALSE
, time
);
762 on_path_entry_icon_release (GtkEntry
*entry
,
763 GtkEntryIconPosition position
,
767 if (position
== GTK_ENTRY_ICON_SECONDARY
)
769 if (self
->priv
->path
)
771 g_free (self
->priv
->path
);
772 self
->priv
->path
= NULL
;
780 git_log_pane_init (GitLogPane
*self
)
782 gchar
*objects
[] = {"log_pane",
783 "log_branch_combo_model",
787 GError
*error
= NULL
;
789 GtkWidget
*path_entry
;
790 GtkTreeView
*log_view
;
791 GtkTreeViewColumn
*ref_icon_column
;
792 GtkTreeViewColumn
*graph_column
;
793 GtkTreeViewColumn
*short_log_column
;
794 GtkTreeViewColumn
*author_column
;
795 GtkTreeViewColumn
*date_column
;
796 GtkCellRenderer
*ref_icon_renderer
;
797 GtkCellRenderer
*short_log_renderer
;
798 GtkCellRenderer
*author_renderer
;
799 GtkCellRenderer
*date_renderer
;
800 GtkTreeViewColumn
*loading_spinner_column
;
801 GtkCellRenderer
*loading_spinner_renderer
;
802 GtkCellRenderer
*loading_indicator_renderer
;
804 GValue cycle_duration_value
= {0,};
805 GValue num_steps_value
= {0,};
806 GtkComboBox
*branch_combo
;
807 GtkTreeSelection
*selection
;
809 self
->priv
= g_new0 (GitLogPanePriv
, 1);
810 self
->priv
->builder
= gtk_builder_new ();
812 if (!gtk_builder_add_objects_from_file (self
->priv
->builder
, BUILDER_FILE
,
816 g_warning ("Couldn't load builder file: %s", error
->message
);
817 g_error_free (error
);
820 log_pane
= GTK_WIDGET (gtk_builder_get_object (self
->priv
->builder
,
822 path_entry
= GTK_WIDGET (gtk_builder_get_object (self
->priv
->builder
,
824 log_view
= GTK_TREE_VIEW (gtk_builder_get_object (self
->priv
->builder
,
826 ref_icon_column
= GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self
->priv
->builder
,
828 graph_column
= GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self
->priv
->builder
,
830 short_log_column
= GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self
->priv
->builder
,
831 "short_log_column"));
832 author_column
= GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self
->priv
->builder
,
834 date_column
= GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self
->priv
->builder
,
836 ref_icon_renderer
= GTK_CELL_RENDERER (gtk_builder_get_object (self
->priv
->builder
,
837 "ref_icon_renderer"));
838 author_renderer
= GTK_CELL_RENDERER (gtk_builder_get_object (self
->priv
->builder
,
840 date_renderer
= GTK_CELL_RENDERER (gtk_builder_get_object (self
->priv
->builder
,
842 branch_combo
= GTK_COMBO_BOX (gtk_builder_get_object (self
->priv
->builder
,
844 loading_spinner_column
= GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self
->priv
->builder
,
845 "loading_spinner_column"));
846 selection
= gtk_tree_view_get_selection (log_view
);
849 g_signal_connect (G_OBJECT (path_entry
), "icon-release",
850 G_CALLBACK (on_path_entry_icon_release
),
853 /* Set up the log model */
854 self
->priv
->log_model
= gtk_list_store_new (1, GIT_TYPE_REVISION
);
856 /* Ref icon column */
857 gtk_tree_view_column_set_cell_data_func (ref_icon_column
, ref_icon_renderer
,
858 (GtkTreeCellDataFunc
) ref_icon_cell_function
,
863 self
->priv
->graph_renderer
= giggle_graph_renderer_new ();
865 gtk_tree_view_column_pack_start (graph_column
, self
->priv
->graph_renderer
,
867 gtk_tree_view_column_add_attribute (graph_column
, self
->priv
->graph_renderer
,
870 /* Short log column. We have to create this render ouselves becuause Glade
871 * doesn't seem to give us to option to pack it with expand */
872 short_log_renderer
= gtk_cell_renderer_text_new ();
873 g_object_set (G_OBJECT (short_log_renderer
), "ellipsize",
874 PANGO_ELLIPSIZE_END
, NULL
);
875 gtk_tree_view_column_pack_start (short_log_column
, short_log_renderer
,
877 gtk_tree_view_column_set_cell_data_func (short_log_column
, short_log_renderer
,
878 (GtkTreeCellDataFunc
) short_log_cell_function
,
882 gtk_tree_view_column_set_cell_data_func (author_column
, author_renderer
,
883 (GtkTreeCellDataFunc
) author_cell_function
,
887 gtk_tree_view_column_set_cell_data_func (date_column
, date_renderer
,
888 (GtkTreeCellDataFunc
) date_cell_function
,
891 gtk_tree_view_set_model (log_view
, GTK_TREE_MODEL (self
->priv
->log_model
));
893 /* Ref icon tooltip */
894 g_signal_connect (G_OBJECT (log_view
), "query-tooltip",
895 G_CALLBACK (on_log_view_query_tooltip
),
898 /* Loading indicator. The loading indicator is a second tree view display
899 * that looks just like the real log display, except that it displays a
900 * spinner renderer and the text "Loading..." in the Short Log column. */
901 self
->priv
->log_loading_model
= GTK_LIST_STORE (gtk_builder_get_object (self
->priv
->builder
,
902 "log_loading_model"));
903 loading_spinner_renderer
= gtk_cell_renderer_spinner_new ();
904 loading_indicator_renderer
= gtk_cell_renderer_text_new ();
906 g_object_set (G_OBJECT (loading_spinner_renderer
), "active", TRUE
, NULL
);
908 gtk_tree_view_column_pack_start (loading_spinner_column
,
909 loading_spinner_renderer
, FALSE
);
910 gtk_tree_view_column_pack_start (loading_spinner_column
,
911 loading_indicator_renderer
, TRUE
);
912 gtk_tree_view_column_add_attribute (loading_spinner_column
,
913 loading_spinner_renderer
,
914 "pulse", LOADING_COL_PULSE
);
915 gtk_tree_view_column_add_attribute (loading_spinner_column
,
916 loading_indicator_renderer
,
917 "text", LOADING_COL_INDICATOR
);
920 gtk_tree_view_enable_model_drag_source (log_view
,
923 G_N_ELEMENTS (drag_source_targets
),
926 g_signal_connect (G_OBJECT (log_view
), "drag-data-get",
927 G_CALLBACK (on_log_view_drag_data_get
),
930 /* DnD target. Use this as a means of selecting a file to view the
931 * log of. Files or folders would normally be dragged in from the file
932 * manager, but they can come from any source that supports URI's. */
933 gtk_drag_dest_set (log_pane
,
934 GTK_DEST_DEFAULT_MOTION
| GTK_DEST_DEFAULT_HIGHLIGHT
,
936 G_N_ELEMENTS (drag_target_targets
),
937 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
939 g_signal_connect (G_OBJECT (log_pane
), "drag-data-received",
940 G_CALLBACK (on_log_pane_drag_data_received
),
943 g_signal_connect (G_OBJECT (log_pane
), "drag-drop",
944 G_CALLBACK (on_log_pane_drag_drop
),
947 /* The loading view always has one row. Cache a copy of its iter for easy
949 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self
->priv
->log_loading_model
),
950 &(self
->priv
->spinner_iter
));
952 /* Get some information about spinner cycles from the theme */
953 style
= gtk_widget_get_style (GTK_WIDGET (log_view
));
954 g_value_init (&cycle_duration_value
, G_TYPE_UINT
);
955 g_value_init (&num_steps_value
, G_TYPE_UINT
);
957 gtk_style_get_style_property (style
, GTK_TYPE_SPINNER
, "cycle-duration",
958 &cycle_duration_value
);
959 gtk_style_get_style_property (style
, GTK_TYPE_SPINNER
, "num_steps",
962 self
->priv
->spin_cycle_duration
= g_value_get_uint (&cycle_duration_value
);
963 self
->priv
->spin_cycle_steps
= g_value_get_uint (&num_steps_value
);
965 g_value_unset (&cycle_duration_value
);
966 g_value_unset (&num_steps_value
);
968 g_object_set (G_OBJECT (loading_spinner_renderer
), "active", TRUE
, NULL
);
970 /* Log message display */
971 gtk_tree_selection_set_select_function (selection
,
972 (GtkTreeSelectionFunc
) on_log_view_row_selected
,
975 /* Branch handling */
976 self
->priv
->branches_table
= g_hash_table_new_full (g_str_hash
, g_str_equal
,
979 g_signal_connect (G_OBJECT (branch_combo
), "changed",
980 G_CALLBACK (on_branch_combo_changed
),
986 git_log_pane_finalize (GObject
*object
)
990 self
= GIT_LOG_PANE (object
);
992 g_object_unref (self
->priv
->builder
);
993 g_free (self
->priv
->path
);
994 g_hash_table_destroy (self
->priv
->branches_table
);
995 g_hash_table_unref (self
->priv
->refs
);
996 g_free (self
->priv
->selected_branch
);
999 G_OBJECT_CLASS (git_log_pane_parent_class
)->finalize (object
);
1003 git_log_pane_get_widget (AnjutaDockPane
*pane
)
1007 self
= GIT_LOG_PANE (pane
);
1009 return GTK_WIDGET (gtk_builder_get_object (self
->priv
->builder
,
1014 git_log_pane_class_init (GitLogPaneClass
*klass
)
1016 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
1017 AnjutaDockPaneClass
*pane_class
= ANJUTA_DOCK_PANE_CLASS (klass
);
1019 object_class
->finalize
= git_log_pane_finalize
;
1020 pane_class
->get_widget
= git_log_pane_get_widget
;
1021 pane_class
->refresh
= NULL
;
1026 git_log_pane_new (Git
*plugin
)
1030 self
= g_object_new (GIT_TYPE_LOG_PANE
, "plugin", plugin
, NULL
);
1032 g_signal_connect (G_OBJECT (plugin
->ref_command
), "command-finished",
1033 G_CALLBACK (on_ref_command_finished
),
1036 return ANJUTA_DOCK_PANE (self
);
1040 git_log_pane_set_working_directory (GitLogPane
*self
,
1041 const gchar
*working_directory
)
1043 /* TODO: Add public function implementation here */
1047 git_log_pane_get_selected_revision (GitLogPane
*self
)
1049 GtkTreeView
*log_view
;
1050 GtkTreeSelection
*selection
;
1051 GitRevision
*revision
;
1054 log_view
= GTK_TREE_VIEW (gtk_builder_get_object (self
->priv
->builder
,
1056 selection
= gtk_tree_view_get_selection (log_view
);
1059 if (gtk_tree_selection_get_selected (selection
, NULL
, &iter
))
1061 gtk_tree_model_get (GTK_TREE_MODEL (self
->priv
->log_model
), &iter
,
1062 LOG_COL_REVISION
, &revision
, -1);