Updated Swedish translation
[anjuta.git] / plugins / git / git-log-pane.c
blobe1592140a4d9675051c0ed1e3350b904b0175973
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * anjuta
4 * Copyright (C) James Liggett 2010 <jrliggett@cox.net>
5 *
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"
22 enum
24 LOG_COL_REVISION
27 enum
29 LOADING_COL_PULSE,
30 LOADING_COL_INDICATOR
33 /* Loading view modes */
34 typedef enum
36 /* Show the real log viewer */
37 LOG_VIEW_NORMAL,
39 /* Show the loading view */
40 LOG_VIEW_LOADING
41 } LogViewMode;
43 enum
45 BRANCH_COL_ACTIVE,
46 BRANCH_COL_ACTIVE_ICON,
47 BRANCH_COL_NAME
50 /* DnD source targets */
51 static GtkTargetEntry drag_source_targets[] =
54 "STRING",
60 /* DnD target targets */
61 static GtkTargetEntry drag_target_targets[] =
64 "text/uri-list",
70 struct _GitLogPanePriv
72 GtkBuilder *builder;
73 GtkListStore *log_model;
74 GtkCellRenderer *graph_renderer;
75 GHashTable *refs;
76 gchar *path;
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;
91 gint spin_timer_id;
92 GtkListStore *log_loading_model;
93 GtkTreeIter spinner_iter;
95 /* Commands */
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);
103 static void
104 on_branch_list_command_started (AnjutaCommand *command,
105 GitLogPane *self)
107 GtkComboBox *branch_combo;
108 GtkListStore *log_branch_combo_model;
110 branch_combo = GTK_COMBO_BOX (gtk_builder_get_object (self->priv->builder,
111 "branch_combo"));
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);
121 static void
122 on_branch_list_command_finished (AnjutaCommand *command,
123 guint return_code,
124 GitLogPane *self)
126 GtkComboBox *branch_combo;
127 GtkTreeModel *log_branch_combo_model;
128 GtkTreePath *path;
129 GtkTreeIter iter;
131 branch_combo = GTK_COMBO_BOX (gtk_builder_get_object (self->priv->builder,
132 "branch_combo"));
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
140 * one */
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,
145 (gpointer) &path)))
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);
160 static void
161 on_branch_list_command_data_arrived (AnjutaCommand *command,
162 GitLogPane *self)
164 GtkListStore *log_branch_combo_model;
165 GList *current_branch;
166 GitBranch *branch;
167 gchar *name;
168 GtkTreeIter iter;
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,
186 -1);
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),
192 &iter);
194 else
196 gtk_list_store_set (log_branch_combo_model, &iter,
197 BRANCH_COL_ACTIVE, FALSE,
198 BRANCH_COL_ACTIVE_ICON, NULL,
199 -1);
202 gtk_list_store_set (log_branch_combo_model, &iter,
203 BRANCH_COL_NAME, name,
204 -1);
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),
207 &iter));
209 g_free (name);
211 current_branch = g_list_next (current_branch);
216 static gboolean
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;
221 else
222 self->priv->current_spin_count++;
224 gtk_list_store_set (self->priv->log_loading_model,
225 &(self->priv->spinner_iter),
226 LOADING_COL_PULSE,
227 self->priv->current_spin_count,
228 -1);
229 return TRUE;
232 static void
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"));
240 switch (mode)
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,
248 self);
251 break;
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,
265 -1);
266 break;
267 default:
268 break;
271 gtk_notebook_set_current_page (loading_notebook, mode);
274 static void
275 on_log_command_finished (AnjutaCommand *command, guint return_code,
276 GitLogPane *self)
278 GtkTreeView *log_view;
279 GQueue *queue;
280 GtkTreeIter iter;
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,
287 "log_view"));
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
293 * empty log.
295 #if 0
296 git_pane_report_errors (command, return_code,
297 ANJUTA_PLUGIN_GIT (anjuta_dock_pane_get_plugin (ANJUTA_DOCK_PANE (self))));
298 #endif
299 goto out;
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,
310 revision, -1);
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));
321 out:
322 g_clear_object (&self->priv->log_command);
325 static void
326 refresh_log (GitLogPane *self)
328 Git *plugin;
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,
334 "log_view"));
335 graph_column = GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self->priv->builder,
336 "graph_column"));
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,
347 self->priv->path,
348 NULL,
349 NULL,
350 NULL,
351 NULL,
352 NULL,
353 NULL);
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);
359 else
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),
364 self, 0);
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));
374 static void
375 on_ref_command_finished (AnjutaCommand *command, guint return_code,
376 GitLogPane *self)
378 Git *plugin;
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),
398 self, 0);
400 g_signal_connect_object (G_OBJECT (self->priv->branch_list_command), "command-finished",
401 G_CALLBACK (on_branch_list_command_finished),
402 self, 0);
404 g_signal_connect_object (G_OBJECT (self->priv->branch_list_command), "data-arrived",
405 G_CALLBACK (on_branch_list_command_data_arrived),
406 self, 0);
408 anjuta_command_start (ANJUTA_COMMAND (self->priv->branch_list_command));
411 static void
412 on_branch_combo_changed (GtkComboBox *combo_box, GitLogPane *self)
414 GtkTreeModel *log_branch_combo_model;
415 gchar *branch;
416 GtkTreeIter iter;
417 gboolean active;
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,
426 -1);
428 self->priv->viewing_active_branch = active;
430 g_free (self->priv->selected_branch);
431 self->priv->selected_branch = g_strdup (branch);
433 g_free (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. */
440 refresh_log (self);
445 static void
446 author_cell_function (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
447 GtkTreeModel *model, GtkTreeIter *iter,
448 gpointer user_data)
450 GitRevision *revision;
451 gchar *author;
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);
460 g_free (author);
463 static void
464 date_cell_function (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
465 GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
467 GitRevision *revision;
468 gchar *date;
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);
477 g_free (date);
480 static void
481 short_log_cell_function (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
482 GtkTreeModel *model, GtkTreeIter *iter,
483 gpointer user_data)
485 GitRevision *revision;
486 gchar *short_log;
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);
495 g_free (short_log);
498 static void
499 ref_icon_cell_function (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
500 GtkTreeModel *model, GtkTreeIter *iter,
501 GitLogPane *self)
503 GitRevision *revision;
504 gchar *sha;
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);
513 else
514 g_object_set (renderer, "stock-id", NULL, NULL);
516 g_free (sha);
519 static gboolean
520 on_log_view_query_tooltip (GtkWidget *log_view, gint x, gint y,
521 gboolean keyboard_mode, GtkTooltip *tooltip,
522 GitLogPane *self)
524 gboolean ret;
525 GtkTreeViewColumn *ref_icon_column;
526 gint bin_x;
527 gint bin_y;
528 GtkTreeViewColumn *current_column;
529 GtkTreePath *path;
530 GtkTreeModel *model;
531 GtkTreeIter iter;
532 GitRevision *revision;
533 gchar *sha;
534 GList *ref_list;
535 GList *current_ref;
536 GString *tooltip_string;
537 gchar *ref_name;
538 GitRefType ref_type;
540 ret = FALSE;
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, &current_column, NULL,
548 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);
562 g_free (sha);
564 if (ref_list)
566 current_ref = ref_list;
567 tooltip_string = g_string_new ("");
569 while (current_ref)
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");
577 switch (ref_type)
579 case GIT_REF_TYPE_BRANCH:
580 g_string_append_printf (tooltip_string,
581 _("<b>Branch:</b> %s"),
582 ref_name );
583 break;
584 case GIT_REF_TYPE_TAG:
585 g_string_append_printf (tooltip_string,
586 _("<b>Tag:</b> %s"),
587 ref_name);
588 break;
589 case GIT_REF_TYPE_REMOTE:
590 g_string_append_printf (tooltip_string,
591 _("<b>Remote:</b> %s"),
592 ref_name);
593 break;
594 default:
595 break;
598 g_free (ref_name);
599 current_ref = g_list_next (current_ref);
602 gtk_tooltip_set_markup (tooltip, tooltip_string->str);
603 g_string_free (tooltip_string, TRUE);
605 ret = TRUE;
609 gtk_tree_path_free (path);
612 return ret;
615 static void
616 on_log_message_command_finished (AnjutaCommand *command, guint return_code,
617 GitLogPane *self)
619 GtkWidget *log_text_view;
620 GtkTextBuffer *buffer;
621 gchar *log_message;
623 log_text_view = GTK_WIDGET (gtk_builder_get_object (self->priv->builder,
624 "log_text_view"));
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);
635 static gboolean
636 on_log_view_row_selected (GtkTreeSelection *selection,
637 GtkTreeModel *model,
638 GtkTreePath *path,
639 gboolean path_currently_selected,
640 GitLogPane *self)
642 Git *plugin;
643 GtkTreeIter iter;
644 GitRevision *revision;
645 gchar *sha;
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);
662 g_free (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),
667 self, 0);
669 anjuta_command_start (ANJUTA_COMMAND (self->priv->log_message_command));
672 return TRUE;
675 static void
676 on_log_view_drag_data_get (GtkWidget *log_view,
677 GdkDragContext *drag_context,
678 GtkSelectionData *data,
679 guint info, guint time,
680 GitLogPane *self)
682 GtkTreeSelection *selection;
683 GtkTreeIter iter;
684 GitRevision *revision;
685 gchar *sha;
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,
694 0, &revision, -1);
696 sha = git_revision_get_sha (revision);
698 gtk_selection_data_set_text (data, sha, -1);
700 g_object_unref (revision);
701 g_free (sha);
705 static void
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)
711 Git *plugin;
712 AnjutaEntry *path_entry;
713 gboolean success;
714 gchar **uri_list;
715 GFile *parent_file;
716 GFile *file;
717 gchar *path;
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,
721 "path_entry"));
722 success = FALSE;
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);
730 parent_file = NULL;
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]);
737 if (parent_file)
739 path = g_file_get_relative_path (parent_file, file);
741 g_object_unref (parent_file);
743 else
744 path = g_file_get_path (file);
746 if (path)
748 anjuta_entry_set_text (path_entry, path);
750 g_free (self->priv->path);
751 self->priv->path = g_strdup (path);
753 refresh_log (self);
755 g_free (path);
758 success = TRUE;
760 g_object_unref (file);
761 g_strfreev (uri_list);
765 /* Do not delete source data */
766 gtk_drag_finish (context, success, FALSE, time);
769 static gboolean
770 on_log_pane_drag_drop (GtkWidget *widget, GdkDragContext *context,
771 gint x, gint y, guint time,
772 GitLogPane *self)
774 GdkAtom target_type;
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);
780 else
781 gtk_drag_finish (context, FALSE, FALSE, time);
783 return TRUE;
786 static void
787 on_path_entry_icon_release (GtkEntry *entry,
788 GtkEntryIconPosition position,
789 GdkEvent *event,
790 GitLogPane *self)
792 if (position == GTK_ENTRY_ICON_SECONDARY)
794 if (self->priv->path)
796 g_free (self->priv->path);
797 self->priv->path = NULL;
799 refresh_log (self);
804 static gboolean
805 on_log_view_button_press_event (GtkWidget *log_view, GdkEventButton *event,
806 GitLogPane *self)
808 GtkMenu *menu;
809 GtkTreeSelection *selection;
810 AnjutaPlugin *plugin;
811 AnjutaUI *ui;
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),
823 "/GitLogPopup"));
825 gtk_menu_popup (menu, NULL, NULL, NULL, NULL, event->button,
826 event->time);
830 return FALSE;
833 static void
834 git_log_pane_init (GitLogPane *self)
836 gchar *objects[] = {"log_pane",
837 "log_branch_combo_model",
838 "log_loading_model",
839 "find_button_image",
840 NULL};
841 GError *error = NULL;
842 GtkWidget *log_pane;
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,
864 objects,
865 &error))
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,
872 "log_pane"));
873 path_entry = GTK_WIDGET (gtk_builder_get_object (self->priv->builder,
874 "path_entry"));
875 log_view = GTK_TREE_VIEW (gtk_builder_get_object (self->priv->builder,
876 "log_view"));
877 ref_icon_column = GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self->priv->builder,
878 "ref_icon_column"));
879 graph_column = GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self->priv->builder,
880 "graph_column"));
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,
884 "author_column"));
885 date_column = GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (self->priv->builder,
886 "date_column"));
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,
890 "author_renderer"));
891 date_renderer = GTK_CELL_RENDERER (gtk_builder_get_object (self->priv->builder,
892 "date_renderer"));
893 branch_combo = GTK_COMBO_BOX (gtk_builder_get_object (self->priv->builder,
894 "branch_combo"));
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);
899 /* Path entry */
900 g_signal_connect (G_OBJECT (path_entry), "icon-release",
901 G_CALLBACK (on_path_entry_icon_release),
902 self);
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,
910 self, NULL);
913 /* Graph column */
914 self->priv->graph_renderer = giggle_graph_renderer_new ();
916 gtk_tree_view_column_pack_start (graph_column, self->priv->graph_renderer,
917 TRUE);
918 gtk_tree_view_column_add_attribute (graph_column, self->priv->graph_renderer,
919 "revision", 0);
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,
927 TRUE);
928 gtk_tree_view_column_set_cell_data_func (short_log_column, short_log_renderer,
929 (GtkTreeCellDataFunc) short_log_cell_function,
930 NULL, NULL);
932 /* Author column */
933 gtk_tree_view_column_set_cell_data_func (author_column, author_renderer,
934 (GtkTreeCellDataFunc) author_cell_function,
935 NULL, NULL);
937 /* Date column */
938 gtk_tree_view_column_set_cell_data_func (date_column, date_renderer,
939 (GtkTreeCellDataFunc) date_cell_function,
940 NULL, NULL);
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),
947 self);
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);
970 /* DnD source */
971 gtk_tree_view_enable_model_drag_source (log_view,
972 GDK_BUTTON1_MASK,
973 drag_source_targets,
974 G_N_ELEMENTS (drag_source_targets),
975 GDK_ACTION_COPY);
977 g_signal_connect (G_OBJECT (log_view), "drag-data-get",
978 G_CALLBACK (on_log_view_drag_data_get),
979 self);
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,
986 drag_target_targets,
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),
992 self);
994 g_signal_connect (G_OBJECT (log_pane), "drag-drop",
995 G_CALLBACK (on_log_pane_drag_drop),
996 self);
998 /* Pop up menu */
999 g_signal_connect (G_OBJECT (log_view), "button-press-event",
1000 G_CALLBACK (on_log_view_button_press_event),
1001 self);
1003 /* The loading view always has one row. Cache a copy of its iter for easy
1004 * access. */
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,
1017 self, NULL);
1019 /* Branch handling */
1020 self->priv->branches_table = g_hash_table_new_full (g_str_hash, g_str_equal,
1021 g_free,
1022 (GDestroyNotify) gtk_tree_path_free);
1024 g_signal_connect (G_OBJECT (branch_combo), "changed",
1025 G_CALLBACK (on_branch_combo_changed),
1026 self);
1030 static void
1031 git_log_pane_finalize (GObject *object)
1033 GitLogPane *self;
1034 Git *plugin;
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);
1069 static GtkWidget *
1070 git_log_pane_get_widget (AnjutaDockPane *pane)
1072 GitLogPane *self;
1074 self = GIT_LOG_PANE (pane);
1076 return GTK_WIDGET (gtk_builder_get_object (self->priv->builder,
1077 "log_pane"));
1080 static void
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;
1092 AnjutaDockPane *
1093 git_log_pane_new (Git *plugin)
1095 GitLogPane *self;
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),
1101 self, 0);
1103 return ANJUTA_DOCK_PANE (self);
1106 void
1107 git_log_pane_set_working_directory (GitLogPane *self,
1108 const gchar *working_directory)
1110 /* TODO: Add public function implementation here */
1113 GitRevision *
1114 git_log_pane_get_selected_revision (GitLogPane *self)
1116 GtkTreeView *log_view;
1117 GtkTreeSelection *selection;
1118 GitRevision *revision;
1119 GtkTreeIter iter;
1121 log_view = GTK_TREE_VIEW (gtk_builder_get_object (self->priv->builder,
1122 "log_view"));
1123 selection = gtk_tree_view_get_selection (log_view);
1124 revision = NULL;
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);
1132 return revision;