1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2007 Imendio AB
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 #include <glib/gi18n.h>
26 #include "giggle-window.h"
27 #include "giggle-git.h"
28 #include "giggle-personal-details-window.h"
29 #include "giggle-view-summary.h"
30 #include "giggle-view-history.h"
31 #include "giggle-view-file.h"
32 #include "giggle-searchable.h"
33 #include "giggle-diff-window.h"
34 #include "eggfindbar.h"
36 typedef struct GiggleWindowPriv GiggleWindowPriv
;
38 struct GiggleWindowPriv
{
39 GtkWidget
*content_vbox
;
40 GtkWidget
*main_notebook
;
43 GtkWidget
*summary_view
;
44 GtkWidget
*history_view
;
48 GtkUIManager
*ui_manager
;
49 GtkRecentManager
*recent_manager
;
50 GtkActionGroup
*recent_action_group
;
56 GtkWidget
*personal_details_window
;
57 GtkWidget
*diff_current_window
;
66 static void window_finalize (GObject
*object
);
68 static void window_add_widget_cb (GtkUIManager
*merge
,
70 GiggleWindow
*window
);
72 static void window_action_quit_cb (GtkAction
*action
,
73 GiggleWindow
*window
);
74 static void window_action_open_cb (GtkAction
*action
,
75 GiggleWindow
*window
);
76 static void window_action_save_patch_cb (GtkAction
*action
,
77 GiggleWindow
*window
);
78 static void window_action_diff_cb (GtkAction
*action
,
79 GiggleWindow
*window
);
80 static void window_action_find_cb (GtkAction
*action
,
81 GiggleWindow
*window
);
82 static void window_action_personal_details_cb (GtkAction
*action
,
83 GiggleWindow
*window
);
84 static void window_action_about_cb (GtkAction
*action
,
85 GiggleWindow
*window
);
86 static void window_action_compact_mode_cb (GtkAction
*action
,
87 GiggleWindow
*window
);
88 static void window_directory_changed_cb (GiggleGit
*git
,
90 GiggleWindow
*window
);
91 static void window_recent_repositories_add (GiggleWindow
*window
);
92 static void window_recent_repositories_update (GiggleWindow
*window
);
94 static void window_notebook_switch_page_cb (GtkNotebook
*notebook
,
95 GtkNotebookPage
*page
,
97 GiggleWindow
*window
);
99 static void window_find_next (GtkWidget
*widget
,
100 GiggleWindow
*window
);
101 static void window_find_previous (GtkWidget
*widget
,
102 GiggleWindow
*window
);
104 static const GtkToggleActionEntry toggle_action_entries
[] = {
105 { "CompactMode", NULL
,
106 N_("_Compact mode"), "F7", NULL
,
107 G_CALLBACK (window_action_compact_mode_cb
), FALSE
111 static const GtkActionEntry action_entries
[] = {
112 { "ProjectMenu", NULL
,
113 N_("_Project"), NULL
, NULL
,
117 N_("_Edit"), NULL
, NULL
,
121 N_("_View"), NULL
, NULL
,
125 N_("_Help"), NULL
, NULL
,
128 { "Open", GTK_STOCK_OPEN
,
129 N_("_Open"), "<control>O", N_("Open a GIT repository"),
130 G_CALLBACK (window_action_open_cb
)
132 { "SavePatch", GTK_STOCK_SAVE
,
133 N_("_Save patch"), "<control>S", N_("Save a patch"),
134 G_CALLBACK (window_action_save_patch_cb
)
137 N_("_Diff current changes"), "<control>D", N_("Diff current changes"),
138 G_CALLBACK (window_action_diff_cb
)
140 { "Quit", GTK_STOCK_QUIT
,
141 N_("_Quit"), "<control>Q", N_("Quit the application"),
142 G_CALLBACK (window_action_quit_cb
)
144 { "PersonalDetails", GTK_STOCK_PREFERENCES
,
145 N_("Edit Personal _Details"), NULL
, N_("Edit Personal details"),
146 G_CALLBACK (window_action_personal_details_cb
)
148 { "Find", GTK_STOCK_FIND
,
149 N_("Find..."), NULL
, N_("Find..."),
150 G_CALLBACK (window_action_find_cb
)
152 { "About", GTK_STOCK_ABOUT
,
153 N_("_About"), NULL
, N_("About this application"),
154 G_CALLBACK (window_action_about_cb
)
158 static const gchar
*ui_layout
=
160 " <menubar name='MainMenubar'>"
161 " <menu action='ProjectMenu'>"
162 " <menuitem action='Open'/>"
164 " <menuitem action='SavePatch'/>"
166 " <menuitem action='Diff'/>"
168 " <placeholder name='RecentRepositories'/>"
170 " <menuitem action='Quit'/>"
172 " <menu action='EditMenu'>"
173 " <menuitem action='PersonalDetails'/>"
175 " <menuitem action='Find'/>"
177 " <menu action='ViewMenu'>"
178 " <menuitem action='CompactMode'/>"
180 " <menu action='HelpMenu'>"
181 " <menuitem action='About'/>"
187 G_DEFINE_TYPE (GiggleWindow
, giggle_window
, GTK_TYPE_WINDOW
)
189 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GIGGLE_TYPE_WINDOW, GiggleWindowPriv))
191 #define RECENT_FILES_GROUP "giggle"
192 #define SAVE_PATCH_UI_PATH "/ui/MainMenubar/ProjectMenu/SavePatch"
193 #define FIND_PATH "/ui/MainMenubar/EditMenu/Find"
194 #define RECENT_REPOS_PLACEHOLDER_PATH "/ui/MainMenubar/ProjectMenu/RecentRepositories"
197 giggle_window_class_init (GiggleWindowClass
*class)
199 GObjectClass
*object_class
= G_OBJECT_CLASS (class);
201 object_class
->finalize
= window_finalize
;
203 g_type_class_add_private (object_class
, sizeof (GiggleWindowPriv
));
207 window_create_menu (GiggleWindow
*window
)
209 GiggleWindowPriv
*priv
;
210 GtkActionGroup
*action_group
;
211 GError
*error
= NULL
;
213 priv
= GET_PRIV (window
);
214 priv
->ui_manager
= gtk_ui_manager_new ();
215 g_signal_connect (priv
->ui_manager
,
217 G_CALLBACK (window_add_widget_cb
),
220 action_group
= gtk_action_group_new ("MainActions");
221 gtk_action_group_set_translation_domain (action_group
, GETTEXT_PACKAGE
);
222 gtk_action_group_add_actions (action_group
,
224 G_N_ELEMENTS (action_entries
),
226 gtk_action_group_add_toggle_actions (action_group
,
227 toggle_action_entries
,
228 G_N_ELEMENTS (toggle_action_entries
),
230 gtk_ui_manager_insert_action_group (priv
->ui_manager
, action_group
, 0);
232 gtk_window_add_accel_group (GTK_WINDOW (window
),
233 gtk_ui_manager_get_accel_group (priv
->ui_manager
));
235 g_object_unref (action_group
);
237 gtk_ui_manager_add_ui_from_string (priv
->ui_manager
, ui_layout
, -1, &error
);
239 g_error ("Couldn't create UI: %s\n", error
->message
);
242 gtk_ui_manager_ensure_update (priv
->ui_manager
);
244 /* create recent repositories resources */
245 priv
->recent_action_group
= gtk_action_group_new ("RecentRepositories");
246 gtk_ui_manager_insert_action_group (priv
->ui_manager
, priv
->recent_action_group
, 0);
248 priv
->recent_manager
= gtk_recent_manager_get_default ();
249 g_signal_connect_swapped (priv
->recent_manager
, "changed",
250 G_CALLBACK (window_recent_repositories_update
), window
);
252 window_recent_repositories_update (window
);
256 giggle_window_set_directory (GiggleWindow
*window
,
257 const gchar
*directory
)
259 GiggleWindowPriv
*priv
;
260 GError
*error
= NULL
;
262 priv
= GET_PRIV (window
);
264 if (!giggle_git_set_directory (priv
->git
, directory
, &error
)) {
267 dialog
= gtk_message_dialog_new (GTK_WINDOW (window
),
268 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
271 _("The directory '%s' does not look like a "
272 "GIT repository."), directory
);
274 gtk_dialog_run (GTK_DIALOG (dialog
));
275 gtk_widget_destroy (dialog
);
280 giggle_window_init (GiggleWindow
*window
)
282 GiggleWindowPriv
*priv
;
284 priv
= GET_PRIV (window
);
286 priv
->git
= giggle_git_get ();
288 g_signal_connect (priv
->git
,
290 G_CALLBACK (window_directory_changed_cb
),
292 g_signal_connect_swapped (priv
->git
,
293 "notify::project-dir",
294 G_CALLBACK (window_recent_repositories_add
),
297 priv
->content_vbox
= gtk_vbox_new (FALSE
, 0);
298 gtk_widget_show (priv
->content_vbox
);
299 gtk_container_add (GTK_CONTAINER (window
), priv
->content_vbox
);
301 window_create_menu (window
);
303 priv
->main_notebook
= gtk_notebook_new ();
304 gtk_widget_show (priv
->main_notebook
);
305 gtk_box_pack_start_defaults (GTK_BOX (priv
->content_vbox
), priv
->main_notebook
);
307 g_signal_connect (priv
->main_notebook
, "switch-page",
308 G_CALLBACK (window_notebook_switch_page_cb
), window
);
311 priv
->find_bar
= egg_find_bar_new ();
312 gtk_box_pack_end (GTK_BOX (priv
->content_vbox
), priv
->find_bar
, FALSE
, FALSE
, 0);
314 g_signal_connect (G_OBJECT (priv
->find_bar
), "close",
315 G_CALLBACK (gtk_widget_hide
), NULL
);
316 g_signal_connect (G_OBJECT (priv
->find_bar
), "next",
317 G_CALLBACK (window_find_next
), window
);
318 g_signal_connect (G_OBJECT (priv
->find_bar
), "previous",
319 G_CALLBACK (window_find_previous
), window
);
321 /* personal details window */
322 priv
->personal_details_window
= giggle_personal_details_window_new ();
324 gtk_window_set_transient_for (GTK_WINDOW (priv
->personal_details_window
),
325 GTK_WINDOW (window
));
326 g_signal_connect_after (G_OBJECT (priv
->personal_details_window
), "response",
327 G_CALLBACK (gtk_widget_hide
), NULL
);
329 /* diff current window */
330 priv
->diff_current_window
= giggle_diff_window_new ();
332 gtk_window_set_transient_for (GTK_WINDOW (priv
->diff_current_window
),
333 GTK_WINDOW (window
));
334 g_signal_connect_after (G_OBJECT (priv
->diff_current_window
), "response",
335 G_CALLBACK (gtk_widget_hide
), NULL
);
337 /* append history view */
338 priv
->history_view
= giggle_view_history_new ();
339 gtk_widget_show (priv
->history_view
);
341 gtk_notebook_append_page (GTK_NOTEBOOK (priv
->main_notebook
),
343 gtk_label_new (_("History")));
345 /* append file view */
347 priv->file_view = giggle_view_file_new ();
348 gtk_widget_show (priv->file_view);
350 gtk_notebook_append_page (GTK_NOTEBOOK (priv->main_notebook),
352 gtk_label_new (_("Files")));
355 /* append summary view */
356 priv
->summary_view
= giggle_view_summary_new ();
357 gtk_widget_show (priv
->summary_view
);
359 gtk_notebook_append_page (GTK_NOTEBOOK (priv
->main_notebook
),
361 gtk_label_new (_("Summary")));
366 window_finalize (GObject
*object
)
368 GiggleWindowPriv
*priv
;
370 priv
= GET_PRIV (object
);
372 g_object_unref (priv
->ui_manager
);
373 g_object_unref (priv
->git
);
374 g_object_unref (priv
->recent_manager
);
375 g_object_unref (priv
->recent_action_group
);
377 G_OBJECT_CLASS (giggle_window_parent_class
)->finalize (object
);
381 window_recent_repositories_add (GiggleWindow
*window
)
383 static gchar
*groups
[] = { RECENT_FILES_GROUP
, NULL
};
384 GiggleWindowPriv
*priv
;
386 const gchar
*repository
;
389 priv
= GET_PRIV (window
);
391 repository
= giggle_git_get_project_dir (priv
->git
);
393 repository
= giggle_git_get_git_dir (priv
->git
);
396 g_return_if_fail (repository
!= NULL
);
398 data
= g_slice_new0 (GtkRecentData
);
399 data
->display_name
= g_strdup (giggle_git_get_project_name (priv
->git
));
400 data
->groups
= groups
;
401 data
->mime_type
= g_strdup ("x-directory/normal");
402 data
->app_name
= (gchar
*) g_get_application_name ();
403 data
->app_exec
= g_strjoin (" ", g_get_prgname (), "%u", NULL
);
405 tmp_string
= g_filename_to_uri (repository
, NULL
, NULL
);
406 gtk_recent_manager_add_full (priv
->recent_manager
,
412 window_recent_repository_activate (GtkAction
*action
,
413 GiggleWindow
*window
)
415 GiggleWindowPriv
*priv
;
416 const gchar
*directory
;
418 priv
= GET_PRIV (window
);
420 directory
= g_object_get_data (G_OBJECT (action
), "recent-action-path");
421 giggle_window_set_directory (window
, directory
);
425 window_recent_repositories_clear (GiggleWindow
*window
)
427 GiggleWindowPriv
*priv
;
430 priv
= GET_PRIV (window
);
431 actions
= l
= gtk_action_group_list_actions (priv
->recent_action_group
);
433 for (l
= actions
; l
!= NULL
; l
= l
->next
) {
434 gtk_action_group_remove_action (priv
->recent_action_group
, l
->data
);
437 g_list_free (actions
);
440 /* this should not be necessary when there's
441 * GtkRecentManager/GtkUIManager integration
444 window_recent_repositories_reload (GiggleWindow
*window
)
446 GiggleWindowPriv
*priv
;
447 GList
*recent_items
, *l
;
450 guint recent_menu_id
;
451 gchar
*action_name
, *label
;
454 priv
= GET_PRIV (window
);
456 recent_items
= l
= gtk_recent_manager_get_items (priv
->recent_manager
);
457 recent_menu_id
= gtk_ui_manager_new_merge_id (priv
->ui_manager
);
459 /* FIXME: the max count is hardcoded */
460 while (l
&& count
< 10) {
463 if (gtk_recent_info_has_group (info
, RECENT_FILES_GROUP
)) {
464 action_name
= g_strdup_printf ("recent-repository-%d", count
);
465 label
= g_strdup (gtk_recent_info_get_uri_display (info
));
467 /* FIXME: add accel? */
469 action
= gtk_action_new (action_name
,
474 g_object_set_data_full (G_OBJECT (action
), "recent-action-path",
475 g_strdup (gtk_recent_info_get_uri_display (info
)),
476 (GDestroyNotify
) g_free
);
478 g_signal_connect (action
,
480 G_CALLBACK (window_recent_repository_activate
),
483 gtk_action_group_add_action (priv
->recent_action_group
, action
);
485 gtk_ui_manager_add_ui (priv
->ui_manager
,
487 RECENT_REPOS_PLACEHOLDER_PATH
,
490 GTK_UI_MANAGER_MENUITEM
,
493 g_object_unref (action
);
494 g_free (action_name
);
502 g_list_foreach (recent_items
, (GFunc
) gtk_recent_info_unref
, NULL
);
503 g_list_free (recent_items
);
507 window_recent_repositories_update (GiggleWindow
*window
)
509 window_recent_repositories_clear (window
);
510 window_recent_repositories_reload (window
);
513 /* Update revision info. If previous_revision is not NULL, a diff between it and
514 * the current revision will be shown.
518 window_add_widget_cb (GtkUIManager
*merge
,
520 GiggleWindow
*window
)
522 GiggleWindowPriv
*priv
;
524 priv
= GET_PRIV (window
);
526 gtk_box_pack_start (GTK_BOX (priv
->content_vbox
), widget
, FALSE
, FALSE
, 0);
530 window_action_quit_cb (GtkAction
*action
,
531 GiggleWindow
*window
)
537 window_action_open_cb (GtkAction
*action
,
538 GiggleWindow
*window
)
540 GiggleWindowPriv
*priv
;
541 GtkWidget
*file_chooser
;
542 const gchar
*directory
;
544 priv
= GET_PRIV (window
);
546 file_chooser
= gtk_file_chooser_dialog_new (
547 _("Select GIT repository"),
549 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
,
550 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
551 GTK_STOCK_OK
, GTK_RESPONSE_OK
,
554 gtk_file_chooser_set_current_folder (
555 GTK_FILE_CHOOSER (file_chooser
),
556 giggle_git_get_directory (priv
->git
));
558 if (gtk_dialog_run (GTK_DIALOG (file_chooser
)) == GTK_RESPONSE_OK
) {
559 directory
= gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (file_chooser
));
560 giggle_window_set_directory (window
, directory
);
563 gtk_widget_destroy (file_chooser
);
567 window_action_save_patch_cb (GtkAction
*action
,
568 GiggleWindow
*window
)
570 /* FIXME: implement this again with GiggleView */
572 GiggleWindowPriv
*priv
;
573 GtkWidget
*file_chooser
;
574 GtkTextBuffer
*text_buffer
;
575 GtkTextIter iter_start
, iter_end
;
577 GError
*error
= NULL
;
579 priv
= GET_PRIV (window
);
581 file_chooser
= gtk_file_chooser_dialog_new (
582 _("Save patch file"),
584 GTK_FILE_CHOOSER_ACTION_SAVE
,
585 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
586 GTK_STOCK_OK
, GTK_RESPONSE_OK
,
589 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (file_chooser
), TRUE
);
591 /* FIXME: remember the last selected folder */
593 if (gtk_dialog_run (GTK_DIALOG (file_chooser
)) == GTK_RESPONSE_OK
) {
594 text_buffer
= gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv
->diff_textview
));
595 gtk_text_buffer_get_bounds (text_buffer
, &iter_start
, &iter_end
);
597 text
= gtk_text_buffer_get_text (text_buffer
, &iter_start
, &iter_end
, TRUE
);
598 path
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (file_chooser
));
600 if (!g_file_set_contents (path
, text
, strlen (text
), &error
)) {
601 window_show_error (window
,
602 N_("An error ocurred when saving to file:\n%s"),
604 g_error_free (error
);
608 gtk_widget_destroy (file_chooser
);
613 window_action_diff_cb (GtkAction
*action
,
614 GiggleWindow
*window
)
616 GiggleWindowPriv
*priv
;
618 priv
= GET_PRIV (window
);
620 gtk_widget_show (priv
->diff_current_window
);
624 window_notebook_switch_page_cb (GtkNotebook
*notebook
,
625 GtkNotebookPage
*page
,
627 GiggleWindow
*window
)
629 GiggleWindowPriv
*priv
;
630 GtkWidget
*page_widget
;
633 priv
= GET_PRIV (window
);
634 page_widget
= gtk_notebook_get_nth_page (notebook
, page_num
);
636 action
= gtk_ui_manager_get_action (priv
->ui_manager
, FIND_PATH
);
637 gtk_action_set_sensitive (action
, GIGGLE_IS_SEARCHABLE (page_widget
));
641 window_action_find_cb (GtkAction
*action
,
642 GiggleWindow
*window
)
644 GiggleWindowPriv
*priv
;
646 priv
= GET_PRIV (window
);
648 gtk_widget_show (priv
->find_bar
);
649 gtk_widget_grab_focus (priv
->find_bar
);
653 window_action_compact_mode_cb (GtkAction
*action
,
654 GiggleWindow
*window
)
656 GiggleWindowPriv
*priv
;
659 priv
= GET_PRIV (window
);
660 active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
662 giggle_view_history_set_compact_mode (GIGGLE_VIEW_HISTORY (priv
->history_view
), active
);
666 window_find (GtkWidget
*widget
,
667 GiggleWindow
*window
,
668 GiggleSearchDirection direction
)
670 GiggleWindowPriv
*priv
;
673 const gchar
*search_string
;
675 priv
= GET_PRIV (window
);
676 page_num
= gtk_notebook_get_current_page (GTK_NOTEBOOK (priv
->main_notebook
));
677 page
= gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv
->main_notebook
), page_num
);
679 g_return_if_fail (GIGGLE_IS_SEARCHABLE (page
));
681 search_string
= egg_find_bar_get_search_string (EGG_FIND_BAR (widget
));
682 giggle_searchable_search (GIGGLE_SEARCHABLE (page
),
683 search_string
, direction
);
687 window_find_next (GtkWidget
*widget
,
688 GiggleWindow
*window
)
690 window_find (widget
, window
, GIGGLE_SEARCH_DIRECTION_NEXT
);
694 window_find_previous (GtkWidget
*widget
,
695 GiggleWindow
*window
)
697 window_find (widget
, window
, GIGGLE_SEARCH_DIRECTION_PREV
);
701 window_action_personal_details_cb (GtkAction
*action
,
702 GiggleWindow
*window
)
704 GiggleWindowPriv
*priv
;
706 priv
= GET_PRIV (window
);
708 gtk_widget_show (priv
->personal_details_window
);
712 window_action_about_cb (GtkAction
*action
,
713 GiggleWindow
*window
)
715 const gchar
*authors
[] = {
723 gtk_show_about_dialog (GTK_WINDOW (window
),
725 "copyright", "Copyright \xc2\xa9 2007 Imendio AB",
726 "translator-credits", _("translator-credits"),
733 window_directory_changed_cb (GiggleGit
*git
,
735 GiggleWindow
*window
)
737 GiggleWindowPriv
*priv
;
739 const gchar
*directory
;
741 priv
= GET_PRIV (window
);
743 directory
= giggle_git_get_directory (git
);
744 title
= g_strdup_printf ("%s - Giggle", directory
);
745 gtk_window_set_title (GTK_WINDOW (window
), title
);
750 giggle_window_new (void)
754 window
= g_object_new (GIGGLE_TYPE_WINDOW
, NULL
);
760 giggle_window_get_git (GiggleWindow
*self
)
762 g_return_val_if_fail (GIGGLE_IS_WINDOW (self
), NULL
);
764 return GET_PRIV (self
)->git
;