Fix a crash in the log viewer on plugin activation.
[anjuta-git-plugin.git] / plugins / git / git-log-dialog.c
blob3e065045f9eff283e93862ef567976ee4a430a94
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * anjuta
4 * Copyright (C) James Liggett 2007 <jrliggett@cox.net>
5 *
6 * anjuta is free software.
7 *
8 * You may redistribute it and/or modify it under the terms of the
9 * GNU General Public License, as published by the Free Software
10 * Foundation; either version 2 of the License, or (at your option)
11 * any later version.
13 * anjuta is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 * See the GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with anjuta. If not, write to:
20 * The Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301, USA.
25 #include "git-log-dialog.h"
27 enum
29 COL_REVISION,
31 NUM_COLS
34 typedef struct
36 Git *plugin;
37 GladeXML *gxml;
38 GtkListStore *list_store;
39 GtkCellRenderer *graph_renderer;
40 gchar *path;
41 GHashTable *refs;
42 } LogData;
44 static void
45 author_cell_function (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
46 GtkTreeModel *model, GtkTreeIter *iter,
47 gpointer user_data)
49 GitRevision *revision;
50 gchar *author;
52 gtk_tree_model_get (model, iter, COL_REVISION, &revision, -1);
53 author = git_revision_get_author (revision);
55 g_object_unref (revision);
57 g_object_set (renderer, "text", author, NULL);
59 g_free (author);
62 static void
63 date_cell_function (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
64 GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
66 GitRevision *revision;
67 gchar *date;
69 gtk_tree_model_get (model, iter, COL_REVISION, &revision, -1);
70 date = git_revision_get_formatted_date (revision);
72 g_object_unref (revision);
74 g_object_set (renderer, "text", date, NULL);
76 g_free (date);
79 static void
80 short_log_cell_function (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
81 GtkTreeModel *model, GtkTreeIter *iter,
82 gpointer user_data)
84 GitRevision *revision;
85 gchar *short_log;
87 gtk_tree_model_get (model, iter, COL_REVISION, &revision, -1);
88 short_log = git_revision_get_short_log (revision);
90 g_object_unref (revision);
92 g_object_set (renderer, "text", short_log, NULL);
94 g_free (short_log);
97 static void
98 ref_icon_cell_function (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
99 GtkTreeModel *model, GtkTreeIter *iter,
100 LogData *data)
102 GitRevision *revision;
103 gchar *sha;
105 gtk_tree_model_get (model, iter, COL_REVISION, &revision, -1);
106 sha = git_revision_get_sha (revision);
108 g_object_unref (revision);
110 if (g_hash_table_lookup_extended (data->refs, sha, NULL, NULL))
111 g_object_set (renderer, "stock-id", GTK_STOCK_INFO, NULL);
112 else
113 g_object_set (renderer, "stock-id", "", NULL);
115 g_free (sha);
118 static void
119 create_columns (LogData *data)
121 GtkWidget *log_changes_view;
122 gint font_size;
123 GtkTreeViewColumn *column;
124 GtkCellRenderer *renderer;
126 log_changes_view = glade_xml_get_widget (data->gxml, "log_changes_view");
127 font_size = PANGO_PIXELS (pango_font_description_get_size (GTK_WIDGET (log_changes_view)->style->font_desc));
129 /* Ref info */
130 column = gtk_tree_view_column_new ();
131 gtk_tree_view_append_column (GTK_TREE_VIEW (log_changes_view), column);
132 renderer = gtk_cell_renderer_pixbuf_new ();
133 gtk_tree_view_column_pack_start (column, renderer, TRUE);
134 gtk_tree_view_column_set_cell_data_func (column, renderer,
135 (GtkTreeCellDataFunc) ref_icon_cell_function,
136 data, NULL);
140 /* Graph */
141 column = gtk_tree_view_column_new ();
142 gtk_tree_view_append_column (GTK_TREE_VIEW (log_changes_view), column);
143 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
144 gtk_tree_view_column_set_min_width (column, font_size * 10);
145 gtk_tree_view_column_set_resizable (column, TRUE);
146 gtk_tree_view_column_pack_start (column, data->graph_renderer, TRUE);
147 gtk_tree_view_column_add_attribute (column, data->graph_renderer,
148 "revision", COL_REVISION);
149 gtk_tree_view_column_set_title (column, "Graph");
151 /* Short log */
152 column = gtk_tree_view_column_new ();
153 gtk_tree_view_append_column (GTK_TREE_VIEW (log_changes_view), column);
154 renderer = gtk_cell_renderer_text_new ();
155 g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
156 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
157 gtk_tree_view_column_set_min_width (column, font_size * 10);
158 gtk_tree_view_column_set_resizable (column, TRUE);
159 gtk_tree_view_column_set_expand (column, TRUE);
160 gtk_tree_view_column_pack_start (column, renderer, TRUE);
161 gtk_tree_view_column_set_cell_data_func (column, renderer,
162 (GtkTreeCellDataFunc) short_log_cell_function,
163 NULL, NULL);
164 gtk_tree_view_column_set_title (column, "Short log");
166 /* Author */
167 column = gtk_tree_view_column_new ();
168 gtk_tree_view_append_column (GTK_TREE_VIEW (log_changes_view), column);
169 renderer = gtk_cell_renderer_text_new ();
170 gtk_tree_view_column_set_resizable (column, TRUE);
171 gtk_tree_view_column_pack_start (column, renderer, TRUE);
172 gtk_tree_view_column_set_cell_data_func (column, renderer,
173 (GtkTreeCellDataFunc) author_cell_function,
174 NULL, NULL);
175 gtk_tree_view_column_set_title (column, "Author");
177 /* Date */
178 column = gtk_tree_view_column_new ();
179 gtk_tree_view_append_column (GTK_TREE_VIEW (log_changes_view), column);
180 renderer = gtk_cell_renderer_text_new ();
181 gtk_tree_view_column_set_resizable (column, TRUE);
182 gtk_tree_view_column_pack_start (column, renderer, TRUE);
183 gtk_tree_view_column_set_cell_data_func (column, renderer,
184 (GtkTreeCellDataFunc) date_cell_function,
185 NULL, NULL);
186 gtk_tree_view_column_set_title (column, "Date");
188 gtk_tree_view_set_model (GTK_TREE_VIEW (log_changes_view),
189 GTK_TREE_MODEL (data->list_store));
190 g_object_unref (data->list_store);
194 static void
195 on_log_command_finished (AnjutaCommand *command, guint return_code,
196 LogData *data)
198 GtkWidget *log_changes_view;
199 GQueue *queue;
200 GtkTreeIter iter;
201 GitRevision *revision;
203 log_changes_view = glade_xml_get_widget (data->gxml, "log_changes_view");
205 g_object_ref (data->list_store);
206 gtk_tree_view_set_model (GTK_TREE_VIEW (log_changes_view), NULL);
208 queue = git_log_command_get_output_queue (GIT_LOG_COMMAND (command));
210 while (g_queue_peek_head (queue))
212 revision = g_queue_pop_head (queue);
214 gtk_list_store_append (data->list_store, &iter);
215 gtk_list_store_set (data->list_store, &iter, COL_REVISION, revision, -1);
216 g_object_unref (revision);
219 giggle_graph_renderer_validate_model (GIGGLE_GRAPH_RENDERER (data->graph_renderer),
220 GTK_TREE_MODEL (data->list_store),
221 COL_REVISION);
222 gtk_tree_view_set_model (GTK_TREE_VIEW (log_changes_view),
223 GTK_TREE_MODEL (data->list_store));
224 g_object_unref (data->list_store);
226 g_object_unref (command);
229 static void
230 on_ref_command_finished (AnjutaCommand *command, guint return_code,
231 LogData *data)
233 GitLogCommand *log_command;
234 gint pulse_timer_id;
236 data->refs = git_ref_command_get_refs (GIT_REF_COMMAND (command));
237 log_command = git_log_command_new (data->plugin->project_root_directory);
239 gtk_list_store_clear (data->list_store);
241 pulse_timer_id = status_bar_progress_pulse (data->plugin,
242 _("Git: Retrieving"
243 " log..."));
245 g_signal_connect (G_OBJECT (log_command), "command-finished",
246 G_CALLBACK (stop_status_bar_progress_pulse),
247 GUINT_TO_POINTER (pulse_timer_id));
249 g_signal_connect (G_OBJECT (log_command), "command-finished",
250 G_CALLBACK (on_log_command_finished),
251 data);
253 anjuta_command_start (ANJUTA_COMMAND (log_command));
255 g_object_unref (command);
258 static void
259 on_view_log_button_clicked (GtkButton *button, LogData *data)
261 GitRefCommand *ref_command;
263 if (data->refs)
264 g_hash_table_unref (data->refs);
266 ref_command = git_ref_command_new (data->plugin->project_root_directory);
268 g_signal_connect (G_OBJECT (ref_command), "command-finished",
269 G_CALLBACK (on_ref_command_finished),
270 data);
272 anjuta_command_start (ANJUTA_COMMAND (ref_command));
275 static void
276 on_log_vbox_destroy (GtkObject *log_vbox, LogData *data)
278 g_free (data->path);
279 g_object_unref (data->gxml);
281 if (data->refs)
282 g_hash_table_unref (data->refs);
284 g_free (data);
287 static void
288 on_log_message_command_finished (AnjutaCommand *command, guint return_code,
289 LogData *data)
291 GtkWidget *log_text_view;
292 GtkTextBuffer *buffer;
293 gchar *log_message;
295 log_text_view = glade_xml_get_widget (data->gxml, "log_text_view");
296 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (log_text_view));
297 log_message = git_log_message_command_get_message (GIT_LOG_MESSAGE_COMMAND (command));
299 gtk_text_buffer_set_text (buffer, log_message, strlen (log_message));
301 g_free (log_message);
302 g_object_unref (command);
305 static gboolean
306 on_log_changes_view_row_selected (GtkTreeSelection *selection,
307 GtkTreeModel *model,
308 GtkTreePath *path,
309 gboolean path_currently_selected,
310 LogData *data)
312 GtkTreeIter iter;
313 GitRevision *revision;
314 gchar *sha;
315 GitLogMessageCommand *log_message_command;
317 if (!path_currently_selected)
319 gtk_tree_model_get_iter (model, &iter, path);
320 gtk_tree_model_get (model, &iter, COL_REVISION, &revision, -1);
321 sha = git_revision_get_sha (revision);
323 log_message_command = git_log_message_command_new (data->plugin->project_root_directory,
324 sha);
326 g_free (sha);
327 g_object_unref (revision);
329 g_signal_connect (G_OBJECT (log_message_command), "command-finished",
330 G_CALLBACK (on_log_message_command_finished),
331 data);
333 anjuta_command_start (ANJUTA_COMMAND (log_message_command));
336 return TRUE;
339 static gboolean
340 on_log_changes_view_query_tooltip (GtkWidget *log_changes_view, gint x, gint y,
341 gboolean keyboard_mode, GtkTooltip *tooltip,
342 LogData *data)
344 gboolean ret;
345 GtkTreeViewColumn *ref_column;
346 gint bin_x;
347 gint bin_y;
348 GtkTreeViewColumn *current_column;
349 GtkTreePath *path;
350 GtkTreeModel *model;
351 GtkTreeIter iter;
352 GitRevision *revision;
353 gchar *sha;
354 GList *ref_list;
355 GList *current_ref;
356 GString *tooltip_string;
357 gchar *ref_name;
358 GitRefType ref_type;
360 ret = FALSE;
362 ref_column = gtk_tree_view_get_column (GTK_TREE_VIEW (log_changes_view), 0);
364 gtk_tree_view_convert_widget_to_bin_window_coords (GTK_TREE_VIEW (log_changes_view),
365 x, y, &bin_x, &bin_y);
366 gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (log_changes_view), bin_x,
367 bin_y, &path, &current_column, NULL, NULL);
369 /* We need to be in the ref icon column */
370 if (current_column == ref_column)
372 model = gtk_tree_view_get_model (GTK_TREE_VIEW (log_changes_view));
373 gtk_tree_model_get_iter (model, &iter, path);
375 gtk_tree_model_get (model, &iter, COL_REVISION, &revision, -1);
376 sha = git_revision_get_sha (revision);
378 g_object_unref (revision);
380 ref_list = g_hash_table_lookup (data->refs, sha);
381 g_free (sha);
383 if (ref_list)
385 current_ref = ref_list;
386 tooltip_string = g_string_new ("");
388 while (current_ref)
390 ref_name = git_ref_get_name (GIT_REF (current_ref->data));
391 ref_type = git_ref_get_ref_type (GIT_REF (current_ref->data));
393 if (tooltip_string->len > 0)
394 g_string_append (tooltip_string, "\n");
396 switch (ref_type)
398 case GIT_REF_TYPE_BRANCH:
399 g_string_append_printf (tooltip_string,
400 _("<b>Branch:</b> %s"),
401 ref_name);
402 break;
403 case GIT_REF_TYPE_TAG:
404 g_string_append_printf (tooltip_string,
405 _("<b>Tag:</b> %s"),
406 ref_name);
407 break;
408 case GIT_REF_TYPE_REMOTE:
409 g_string_append_printf (tooltip_string,
410 _("<b>Remote:</b> %s"),
411 ref_name);
412 break;
413 default:
414 break;
417 g_free (ref_name);
418 current_ref = g_list_next (current_ref);
421 gtk_tooltip_set_markup (tooltip, tooltip_string->str);
422 g_string_free (tooltip_string, TRUE);
424 ret = TRUE;
428 gtk_tree_path_free (path);
429 return ret;
434 static gboolean
435 on_log_changes_view_button_press_event (GtkWidget *log_changes_view,
436 GdkEventButton *event,
437 Git *plugin)
439 GtkTreeSelection *selection;
441 if (event->type == GDK_BUTTON_PRESS)
443 if (event->button == 3)
445 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (log_changes_view));
447 if (gtk_tree_selection_count_selected_rows (selection) > 0)
449 gtk_menu_popup (GTK_MENU(plugin->log_popup_menu), NULL, NULL,
450 NULL, NULL, event->button, event->time);
454 return FALSE;
457 GtkWidget *
458 git_log_window_create (Git *plugin)
460 LogData *data;
461 GtkWidget *log_window;
462 GtkWidget *log_vbox;
463 GtkWidget *log_changes_view;
464 GtkWidget *view_log_button;
465 GtkWidget *whole_project_check;
466 GtkWidget *path_entry;
467 GtkTreeSelection *selection;
469 data = g_new0 (LogData, 1);
470 data->gxml = glade_xml_new (GLADE_FILE, "log_window", NULL);
472 data->plugin = plugin;
473 data->path = NULL;
474 data->graph_renderer = giggle_graph_renderer_new ();
476 log_window = glade_xml_get_widget (data->gxml, "log_window");
477 log_vbox = glade_xml_get_widget (data->gxml, "log_vbox");
478 log_changes_view = glade_xml_get_widget (data->gxml, "log_changes_view");
479 whole_project_check = glade_xml_get_widget (data->gxml,
480 "whole_project_check");
481 path_entry = glade_xml_get_widget (data->gxml, "path_entry");
482 view_log_button = glade_xml_get_widget (data->gxml,
483 "view_log_button");
485 g_object_set_data (G_OBJECT (log_vbox), "log-data", data);
487 g_signal_connect (G_OBJECT (log_changes_view), "query-tooltip",
488 G_CALLBACK (on_log_changes_view_query_tooltip),
489 data);
490 g_signal_connect (G_OBJECT (log_changes_view), "button-press-event",
491 G_CALLBACK (on_log_changes_view_button_press_event),
492 plugin);
495 g_signal_connect (G_OBJECT (view_log_button), "clicked",
496 G_CALLBACK (on_view_log_button_clicked),
497 data);
499 g_object_set_data (G_OBJECT (whole_project_check), "file-entry",
500 path_entry);
501 g_signal_connect (G_OBJECT (whole_project_check), "toggled",
502 G_CALLBACK (on_whole_project_toggled), plugin);
504 data->list_store = gtk_list_store_new (NUM_COLS,
505 G_TYPE_OBJECT);
506 create_columns (data);
508 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (log_changes_view));
509 gtk_tree_selection_set_select_function (selection,
510 (GtkTreeSelectionFunc) on_log_changes_view_row_selected,
511 data, NULL);
513 g_signal_connect (G_OBJECT (log_vbox), "destroy",
514 G_CALLBACK (on_log_vbox_destroy),
515 data);
517 g_object_ref (log_vbox);
518 gtk_container_remove (GTK_CONTAINER (log_window), log_vbox);
519 gtk_widget_destroy (log_window);
521 return log_vbox;
524 void
525 on_menu_git_log (GtkAction *action, Git *plugin)
527 anjuta_shell_present_widget (ANJUTA_PLUGIN (plugin)->shell,
528 plugin->log_viewer, NULL);
531 /* TODO: Enable when FM support is implemented */
532 #if 0
533 void
534 on_fm_subversion_log (GtkAction *action, Git *plugin)
536 GtkWidget *path_text_entry;
538 path_text_entry = glade_xml_get_widget (plugin->log_gxml,
539 "path_text_entry");
541 gtk_entry_set_text (GTK_ENTRY path_entry),
542 plugin->fm_current_filename);
544 anjuta_shell_present_widget (ANJUTA_PLUGIN (plugin)->shell,
545 plugin->log_viewer, NULL);
547 #endif
549 void
550 git_log_window_clear (Git *plugin)
552 LogData *data;
553 GtkWidget *log_text_view;
554 GtkTextBuffer *buffer;
556 data = g_object_get_data (G_OBJECT (plugin->log_viewer), "log-data");
557 log_text_view = glade_xml_get_widget (data->gxml, "log_text_view");
558 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (log_text_view));
560 gtk_list_store_clear (data->list_store);
561 gtk_text_buffer_set_text (buffer, "", 0);
564 GitRevision *
565 git_log_get_selected_revision (Git *plugin)
567 LogData *data;
568 GtkWidget *log_changes_view;
569 GitRevision *revision;
570 GtkTreeSelection *selection;
571 GtkTreeModel *model;
572 GtkTreeIter iter;
574 data = g_object_get_data (G_OBJECT (plugin->log_viewer), "log-data");
575 log_changes_view = glade_xml_get_widget (data->gxml, "log_changes_view");
576 revision = NULL;
578 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (log_changes_view));
579 model = gtk_tree_view_get_model (GTK_TREE_VIEW (log_changes_view));
581 gtk_tree_selection_get_selected (selection, NULL, &iter);
582 gtk_tree_model_get (model, &iter, COL_REVISION, &revision, -1);
584 return revision;