/* * ggv-window.c: the ggv shell * * Copyright 2002 - 2005 the Free Software Foundation * * Author: Jaka Mocnik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include "gtkgs.h" #include "gtkchecklist.h" #include "ggv-prefs.h" #include "ggv-window.h" #include "ggvutils.h" #include "ggv-recent.h" #include "recent-files/egg-recent-view.h" #include "recent-files/egg-recent-view-bonobo.h" #include "recent-files/egg-recent-view-gtk.h" #include "recent-files/egg-recent-model.h" #define GGV_POSTSCRIPT_VIEWER_CONTROL_IID "OAFIID:GNOME_GGV_Control" static BonoboWindowClass *parent_class; static GList *window_list = NULL; /* what can be dragged in us... */ enum { TARGET_URI_LIST }; static void set_file_cmds_sensitivity(GgvWindow * win, gboolean sens) { if(NULL == win->uic) return; bonobo_ui_component_set_prop(win->uic, "/commands/FileReload", "sensitive", sens ? "1" : "0", NULL); } static void sync_settings_menu_items(GgvWindow * win) { if(win->uic == NULL) return; bonobo_ui_component_freeze(win->uic, NULL); bonobo_ui_component_set_prop(win->uic, "/commands/SettingsShowMenus", "state", win->show_menus ? "1" : "0", NULL); bonobo_ui_component_set_prop(win->uic, "/commands/SettingsShowToolbar", "state", win->show_toolbar ? "1" : "0", NULL); bonobo_ui_component_set_prop(win->uic, "/commands/SettingsShowSidebar", "state", win->show_sidebar ? "1" : "0", NULL); bonobo_ui_component_set_prop(win->uic, "/commands/SettingsShowStatusbar", "state", win->show_statusbar ? "1" : "0", NULL); bonobo_ui_component_thaw(win->uic, NULL); } static void sync_settings_popup_items(GgvWindow * win) { if(win->popup_uic == NULL) return; bonobo_ui_component_freeze(win->popup_uic, NULL); bonobo_ui_component_set_prop(win->popup_uic, "/commands/SettingsShowMenus", "state", win->show_menus ? "1" : "0", NULL); bonobo_ui_component_set_prop(win->popup_uic, "/commands/SettingsShowToolbar", "state", win->show_toolbar ? "1" : "0", NULL); bonobo_ui_component_set_prop(win->popup_uic, "/commands/SettingsShowSidebar", "state", win->show_sidebar ? "1" : "0", NULL); bonobo_ui_component_set_prop(win->popup_uic, "/commands/SettingsShowStatusbar", "state", win->show_statusbar ? "1" : "0", NULL); bonobo_ui_component_thaw(win->popup_uic, NULL); } static void sync_fullscreen_items(GgvWindow * win) { if(win->uic != NULL) { bonobo_ui_component_set_prop(win->uic, "/commands/ViewFullscreen", "state", win->fullscreen ? "1" : "0", NULL); } if(win->popup_uic != NULL) { bonobo_ui_component_set_prop(win->popup_uic, "/commands/ViewFullscreen", "state", win->fullscreen ? "1" : "0", NULL); } } static void ggv_window_remove_control(GgvWindow * win) { CORBA_Environment ev; CORBA_exception_init(&ev); if(win->ctlframe != NULL && win->control != CORBA_OBJECT_NIL) { bonobo_control_frame_control_deactivate(win->ctlframe); bonobo_control_frame_bind_to_control(win->ctlframe, NULL, &ev); if(BONOBO_EX(&ev)) { g_warning("Could not bind to NIL: '%s'", bonobo_exception_get_text(&ev)); CORBA_exception_free(&ev); return; } } if(win->control != CORBA_OBJECT_NIL) { bonobo_object_release_unref(win->control, NULL); win->control = CORBA_OBJECT_NIL; } if(win->popup_uic) { bonobo_object_unref(win->popup_uic); win->popup_uic = NULL; } if(win->pb) { bonobo_object_release_unref(win->pb, NULL); win->pb = CORBA_OBJECT_NIL; } if(win->filename) { g_free(win->filename); win->filename = NULL; } ggv_window_set_watch_file(win, FALSE); set_file_cmds_sensitivity(win, FALSE); CORBA_exception_free(&ev); } static void ggv_window_set_fullscreen(GgvWindow * win, gboolean fs) { if(win->fullscreen == fs) return; win->fullscreen = fs; if(win->fullscreen) { gint clx, cly, rx, ry, w, h; gdk_window_get_origin(GTK_WIDGET(win)->window, &rx, &ry); gdk_window_get_geometry(GTK_WIDGET(win)->window, &clx, &cly, &w, &h, NULL); win->orig_x = rx - clx; win->orig_y = ry - cly; win->orig_width = w; win->orig_height = h; if(win->show_menus) { bonobo_ui_component_set_prop(win->uic, "/menu", "hidden", "1", NULL); } if(win->show_toolbar) { bonobo_ui_component_set_prop(win->uic, "/Toolbar", "hidden", "1", NULL); } if(win->show_statusbar) { gtk_widget_hide(win->statusbar); } if(win->show_sidebar) { bonobo_ui_component_set_prop(win->uic, "/Sidebar", "hidden", "1", NULL); } win->orig_sm = win->show_menus; win->orig_st = win->show_toolbar; win->orig_ss = win->show_sidebar; win->orig_sss = win->show_statusbar; win->show_menus = win->show_toolbar = win->show_sidebar = win->show_statusbar = FALSE; gtk_window_move(GTK_WINDOW(win), -clx, -cly); gtk_window_resize(GTK_WINDOW(win), gdk_screen_width(), gdk_screen_height()); gtk_window_fullscreen(GTK_WINDOW(win)); } else { win->show_menus = win->orig_sm; win->show_toolbar = win->orig_st; win->show_sidebar = win->orig_ss; win->show_statusbar = win->orig_sss; if(win->show_menus) { bonobo_ui_component_set_prop(win->uic, "/menu", "hidden", "0", NULL); } if(win->show_toolbar) { bonobo_ui_component_set_prop(win->uic, "/Toolbar", "hidden", "0", NULL); } if(win->show_statusbar) { gtk_widget_show(win->statusbar); } if(win->show_sidebar) { bonobo_ui_component_set_prop(win->uic, "/Sidebar", "hidden", "0", NULL); } gtk_window_move(GTK_WINDOW(win), win->orig_x, win->orig_y); gtk_window_resize(GTK_WINDOW(win), win->orig_width, win->orig_height); gtk_window_unfullscreen(GTK_WINDOW(win)); } sync_settings_menu_items(win); sync_settings_popup_items(win); sync_fullscreen_items(win); } static void ggv_window_drag_data_received(GtkWidget * widget, GdkDragContext * context, gint x, gint y, GtkSelectionData * selection_data, guint info, guint time) { GgvWindow *win = GGV_WINDOW(widget); if(info != TARGET_URI_LIST) return; win->uris_to_open = g_strsplit(selection_data->data, "\r\n", 0); if(context->suggested_action == GDK_ACTION_ASK) { GtkWidget *menu = gtk_menu_new(); bonobo_window_add_popup(BONOBO_WINDOW(win), GTK_MENU(menu), "/popups/DnD"); gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, GDK_CURRENT_TIME); } else { GtkWidget *newwin; gchar **uri = win->uris_to_open; if(win->page_count <= 0) newwin = GTK_WIDGET(win); else newwin = NULL; while(*uri && **uri != '\0') { #ifdef DEBUG g_message("URI %s\n", *uri); #endif /* DEBUG */ if(newwin == NULL) newwin = ggv_window_new(); if(ggv_window_load(GGV_WINDOW(newwin), (*uri))) { if(newwin != GTK_WIDGET(win)) gtk_widget_show(newwin); newwin = NULL; } else { GtkWidget *dlg; dlg = gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Unable to load file:\n%s"), *uri); gtk_widget_show(dlg); gtk_dialog_run(GTK_DIALOG(dlg)); gtk_widget_destroy(dlg); } uri++; } if(newwin != NULL && newwin != GTK_WIDGET(win)) ggv_window_close(GGV_WINDOW(newwin)); g_strfreev(win->uris_to_open); win->uris_to_open = NULL; } } static void control_property_changed_handler(BonoboListener * listener, char *event_name, CORBA_any * any, CORBA_Environment * ev, gpointer data) { GgvWindow *win = GGV_WINDOW(data); if(!g_ascii_strcasecmp(event_name, "Bonobo/Property:change:title")) { } else if(!g_ascii_strcasecmp(event_name, "Bonobo/Property:change:status")) { gnome_appbar_set_status(GNOME_APPBAR(win->statusbar), BONOBO_ARG_GET_STRING(any)); } else if(!g_ascii_strcasecmp(event_name, "Bonobo/Property:change:page")) { if(win->current_page != BONOBO_ARG_GET_LONG(any)) win->current_page = BONOBO_ARG_GET_LONG(any); } else if(!g_ascii_strcasecmp(event_name, "Bonobo/Property:change:page_count")) { win->page_count = BONOBO_ARG_GET_LONG(any); set_file_cmds_sensitivity(win, win->page_count > 0); if(win->page_count <= 0) { gtk_window_set_title(GTK_WINDOW(win), _("GGV: no document loaded")); } } } static void verb_FileOpen(BonoboUIComponent * uic, gpointer data, const char *cname) { EggRecentItem *item; EggRecentModel *recent; GSList *uris; GgvWindow *win = GGV_WINDOW(data); GtkWidget *chooser; GtkWidget *newwin; GtkFileFilter *ps_filter, *all_filter; chooser = gtk_file_chooser_dialog_new(_("Select a PostScript document"), GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL); gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chooser), TRUE); gtk_dialog_set_default_response(GTK_DIALOG(chooser), GTK_RESPONSE_OK); /* first: Postscript doc filter */ ps_filter = gtk_file_filter_new(); gtk_file_filter_set_name(ps_filter, _("PostScript Documents")); gtk_file_filter_add_mime_type(ps_filter, "application/postscript"); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(chooser), ps_filter); /* second: all files filter */ all_filter = gtk_file_filter_new(); gtk_file_filter_set_name(all_filter, _("All Files")); gtk_file_filter_add_pattern(all_filter, "*.*"); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(chooser), all_filter); /* postscript is default */ gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(chooser), ps_filter); #if 0 /* this causes unexplained crashes! */ if(win->filename) gtk_file_chooser_set_uri(GTK_FILE_CHOOSER(chooser), win->filename); #endif do { uris = NULL; if(gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_OK) uris = gtk_file_chooser_get_uris(GTK_FILE_CHOOSER(chooser)); else break; } while(NULL == uris); gtk_widget_destroy(chooser); if(uris == NULL) return; if(win->page_count > 0) newwin = NULL; else newwin = GTK_WIDGET(win); while(uris) { if(NULL == newwin) newwin = ggv_window_new(); if(!ggv_window_load(GGV_WINDOW(newwin), (gchar *) uris->data)) { GtkWidget *dlg; dlg = gtk_message_dialog_new (GTK_WINDOW(win), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Unable to load file:\n%s"), (gchar *) uris->data); gtk_widget_show(dlg); gtk_dialog_run(GTK_DIALOG(dlg)); gtk_widget_destroy(dlg); } else { if(GTK_WIDGET(win) != newwin) gtk_widget_show(newwin); recent = ggv_recent_get_model(); item = egg_recent_item_new_from_uri((gchar *) uris->data); egg_recent_item_set_mime_type(item, "application/postscript"); egg_recent_item_add_group(item, "ggv"); egg_recent_model_add_full(recent, item); egg_recent_item_unref(item); newwin = NULL; } g_free(uris->data); uris = uris->next; } if(NULL != newwin && GTK_WIDGET(win) != newwin) ggv_window_close(GGV_WINDOW(newwin)); g_slist_free(uris); } static void verb_FileReload(BonoboUIComponent * uic, gpointer data, const char *cname) { GgvWindow *win = GGV_WINDOW(data); if(!ggv_window_reload(win)) { GtkWidget *dlg; dlg = gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Unable to reload file:\n%s"), win->filename); gtk_widget_show(dlg); gtk_dialog_run(GTK_DIALOG(dlg)); gtk_widget_destroy(dlg); } } static void verb_FileClose(BonoboUIComponent * uic, gpointer data, const char *cname) { GgvWindow *win = GGV_WINDOW(data); ggv_window_close(win); if(window_list == NULL) bonobo_main_quit(); } void ggv_window_destroy_all() { while(window_list) ggv_window_close(GGV_WINDOW(window_list->data)); } static void listener_ViewFullscreen(BonoboUIComponent * uic, const char *path, Bonobo_UIComponent_EventType type, const char *state, gpointer user_data) { GgvWindow *window; gboolean state_f; g_return_if_fail(user_data != NULL); g_return_if_fail(GGV_IS_WINDOW(user_data)); if(type != Bonobo_UIComponent_STATE_CHANGED) return; if(!state) return; window = GGV_WINDOW(user_data); state_f = atoi(state); if(!strcmp(path, "ViewFullscreen")) { if(window->fullscreen != state_f) ggv_window_set_fullscreen(window, state_f); } } static void listener_SettingsShow(BonoboUIComponent * uic, const char *path, Bonobo_UIComponent_EventType type, const char *state, gpointer user_data) { GgvWindow *window; gboolean state_f; g_return_if_fail(user_data != NULL); g_return_if_fail(GGV_IS_WINDOW(user_data)); if(type != Bonobo_UIComponent_STATE_CHANGED) return; if(!state) return; window = GGV_WINDOW(user_data); state_f = atoi(state); if(!strcmp(path, "SettingsShowMenus")) { if(window->show_menus != state_f) { window->show_menus = state_f; bonobo_ui_component_set_prop(window->uic, "/menu", "hidden", state_f ? "0" : "1", NULL); } } else if(!strcmp(path, "SettingsShowSidebar")) { if(window->show_sidebar != state_f) { window->show_sidebar = state_f; bonobo_ui_component_set_prop(window->uic, "/Sidebar", "hidden", state_f ? "0" : "1", NULL); } } else if(!strcmp(path, "SettingsShowToolbar")) { if(window->show_toolbar != state_f) { window->show_toolbar = state_f; bonobo_ui_component_set_prop(window->uic, "/Toolbar", "hidden", state_f ? "0" : "1", NULL); } } else if(!strcmp(path, "SettingsShowStatusbar")) { if(window->show_statusbar != state_f) { window->show_statusbar = state_f; if(state_f) gtk_widget_show(window->statusbar); else gtk_widget_hide(window->statusbar); } } if(uic == window->uic) sync_settings_popup_items(window); else sync_settings_menu_items(window); } static void verb_HelpAbout(BonoboUIComponent * uic, gpointer data, const char *cname) { static GtkWidget *about = NULL; static gboolean translated = FALSE; static const char *authors[] = { N_("Gary Ekker (current maintainer)"), "Jaka Mocnik", "Jonathan Blandford", "Daniel M. German", "Satyajit Kanungo", "Dan E. Kelley", "Werner Koerner", "Tuomas J. Lukka", "Johannes Plass", "Istvan Szekeres", "Tim Theisen", N_("And many more..."), NULL }; static const char *documenters[] = { "Sun Microsystems ", NULL }; char *translator_credits = _("translator-credits"); static GdkPixbuf *logo = NULL; if(!translated) { int i; for(i = 0; authors[i] != NULL; i++) authors[i] = _(authors[i]); translated = TRUE; } if(!logo) logo = gdk_pixbuf_new_from_file(GNOMEICONDIR "/ggv/ggv-splash.png", NULL); if(!about) { about = gnome_about_new(_("Gnome Ghostview"), VERSION, "Copyright \xc2\xa9 1998-2004 Free Software Foundation, Inc.", _("The GNOME PostScript document previewer"), authors, documenters, strcmp(translator_credits, "translator_credits") != 0 ? translator_credits : NULL, logo); gtk_window_set_transient_for(GTK_WINDOW(about), GTK_WINDOW(data)); g_signal_connect(G_OBJECT(about), "destroy", G_CALLBACK(gtk_widget_destroyed), &about); } gtk_widget_show_now(about); ggv_raise_and_focus_widget(about); } static void verb_HelpContents(BonoboUIComponent * uic, gpointer data, const char *cname) { GError *error = NULL; GgvWindow *win = GGV_WINDOW(data); if(win->fullscreen) ggv_window_set_fullscreen(win, FALSE); gnome_help_display("ggv", NULL, &error); } static void verb_DnDNewWindow(BonoboUIComponent * uic, gpointer user_data, const char *cname) { GgvWindow *win = GGV_WINDOW(user_data); gchar **uri; GtkWidget *newwin = NULL; uri = win->uris_to_open; while(*uri && **uri != '\0') { #ifdef DEBUG g_message("URI %s", *uri); #endif /* DEBUG */ if(NULL == newwin) newwin = ggv_window_new(); if(ggv_window_load(GGV_WINDOW(newwin), (*uri))) { gtk_widget_show(newwin); newwin = NULL; } else { GtkWidget *dlg; dlg = gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Unable to load file:\n%s"), *uri); gtk_widget_show(dlg); gtk_dialog_run(GTK_DIALOG(dlg)); gtk_widget_destroy(dlg); } uri++; } if(NULL != newwin) ggv_window_close(GGV_WINDOW(newwin)); g_strfreev(win->uris_to_open); win->uris_to_open = NULL; } static void verb_DnDThisWindow(BonoboUIComponent * uic, gpointer user_data, const char *cname) { GgvWindow *win = GGV_WINDOW(user_data); GtkWidget *newwin; gchar **uri; uri = win->uris_to_open; newwin = GTK_WIDGET(win); while(*uri && **uri != '\0') { #ifdef DEBUG g_message("URI %s", *uri); #endif /* DEBUG */ if(newwin == NULL) newwin = ggv_window_new(); if(ggv_window_load(GGV_WINDOW(newwin), (*uri))) { if(newwin != GTK_WIDGET(win)) gtk_widget_show(newwin); newwin = NULL; } else { GtkWidget *dlg; dlg = gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Unable to load file:\n%s"), *uri); gtk_widget_show(dlg); gtk_dialog_run(GTK_DIALOG(dlg)); gtk_widget_destroy(dlg); } uri++; } if(NULL != newwin && newwin != GTK_WIDGET(win)) ggv_window_close(GGV_WINDOW(newwin)); g_strfreev(win->uris_to_open); win->uris_to_open = NULL; } static void verb_DnDCancel(BonoboUIComponent * uic, gpointer user_data, const char *cname) { GgvWindow *win = GGV_WINDOW(user_data); g_strfreev(win->uris_to_open); win->uris_to_open = NULL; } /* our verb list */ static BonoboUIVerb ggv_app_verbs[] = { BONOBO_UI_VERB("FileClose", verb_FileClose), BONOBO_UI_VERB("FileOpen", verb_FileOpen), BONOBO_UI_VERB("FileReload", verb_FileReload), BONOBO_UI_VERB("HelpAbout", verb_HelpAbout), BONOBO_UI_VERB("Help", verb_HelpContents), BONOBO_UI_VERB("DnDNewWindow", verb_DnDNewWindow), BONOBO_UI_VERB("DnDThisWindow", verb_DnDThisWindow), BONOBO_UI_VERB("DnDCancel", verb_DnDCancel), BONOBO_UI_VERB_END }; static void control_frame_activate_uri(BonoboControlFrame * control_frame, const char *uri, gboolean relative, gpointer data) { GgvWindow *win; g_return_if_fail(uri != NULL); win = GGV_WINDOW(ggv_window_new()); ggv_window_load(win, uri); gtk_widget_show(GTK_WIDGET(win)); } static void ggv_window_destroy(GtkObject * object) { GgvWindow *win; gint w, h; g_return_if_fail(object != NULL); g_return_if_fail(GGV_IS_WINDOW(object)); win = GGV_WINDOW(object); ggv_window_set_watch_file(win, FALSE); if(win->ctlframe != NULL) { bonobo_object_unref(BONOBO_OBJECT(win->ctlframe)); win->ctlframe = NULL; } if(win->control != CORBA_OBJECT_NIL) { bonobo_object_release_unref(win->control, NULL); win->control = CORBA_OBJECT_NIL; } if(win->uic) { bonobo_object_unref(win->uic); win->uic = NULL; } if(win->popup_uic) { bonobo_object_unref(win->popup_uic); win->popup_uic = NULL; } if(win->pb) { bonobo_object_release_unref(win->pb, NULL); win->pb = CORBA_OBJECT_NIL; } if(win->filename) { g_free(win->filename); win->filename = NULL; } window_list = g_list_remove(window_list, win); ggv_get_window_size(GTK_WIDGET(object), &w, &h); if(w > 1 && h > 1) { ggv_default_width = w; ggv_default_height = h; } if(GTK_OBJECT_CLASS(parent_class)->destroy) GTK_OBJECT_CLASS(parent_class)->destroy(object); } static gboolean ggv_window_delete_event(GtkWidget * widget, GdkEventAny * e) { ggv_window_close(GGV_WINDOW(widget)); return TRUE; } static void prefs_changed(GConfClient * client, guint cnxn_id, GConfEntry * entry, gpointer user_data) { GgvWindow *window = GGV_WINDOW(user_data); if(!strcmp(entry->key, "/apps/ggv/layout/showtoolbar")) { gboolean state_f = gconf_value_get_bool(entry->value); if(window->show_toolbar != state_f) { window->show_toolbar = state_f; sync_settings_menu_items(window); sync_settings_popup_items(window); bonobo_ui_component_set_prop(window->uic, "/Toolbar", "hidden", state_f ? "0" : "1", NULL); } } else if(!strcmp(entry->key, "/apps/ggv/layout/showmenubar")) { gboolean state_f = gconf_value_get_bool(entry->value); if(window->show_menus != state_f) { window->show_menus = state_f; sync_settings_menu_items(window); sync_settings_popup_items(window); bonobo_ui_component_set_prop(window->uic, "/menu", "hidden", state_f ? "0" : "1", NULL); } } else if(!strcmp(entry->key, "/apps/ggv/layout/showpanel")) { gboolean state_f = gconf_value_get_bool(entry->value); if(window->show_sidebar != state_f) { window->show_sidebar = state_f; sync_settings_menu_items(window); sync_settings_popup_items(window); bonobo_ui_component_set_prop(window->uic, "/Sidebar", "hidden", state_f ? "0" : "1", NULL); } } else if(!strcmp(entry->key, "/apps/ggv/layout/showstatusbar")) { gboolean state_f = gconf_value_get_bool(entry->value); if(window->show_statusbar != state_f) { window->show_statusbar = state_f; sync_settings_menu_items(window); sync_settings_popup_items(window); if(state_f) gtk_widget_show(window->statusbar); else gtk_widget_hide(window->statusbar); } } else if(!strcmp(entry->key, "/apps/ggv/layout/rightpanel")) { #if 0 bonobo_control_frame_control_deactivate(window->ctlframe); bonobo_control_frame_control_activate(window->ctlframe); #endif } } static void ggv_window_class_init(GgvWindowClass * class) { GObjectClass *gobject_class; GtkObjectClass *object_class; GtkWidgetClass *widget_class; gobject_class = (GObjectClass *) class; object_class = (GtkObjectClass *) class; widget_class = (GtkWidgetClass *) class; parent_class = g_type_class_peek_parent(class); object_class->destroy = ggv_window_destroy; widget_class->delete_event = ggv_window_delete_event; widget_class->drag_data_received = ggv_window_drag_data_received; } static void ggv_window_init(GgvWindow * window) { window_list = g_list_prepend(window_list, window); window->watch_doc = FALSE; window->monitor = NULL; } GType ggv_window_get_type(void) { static GType ggv_window_type = 0; if(!ggv_window_type) { static const GTypeInfo ggv_window_info = { sizeof(GgvWindowClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) ggv_window_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof(GgvWindow), 0, /* n_preallocs */ (GInstanceInitFunc) ggv_window_init, }; ggv_window_type = g_type_register_static(BONOBO_TYPE_WINDOW, "GgvWindow", &ggv_window_info, 0); } return ggv_window_type; } static void menu_position_under_widget(GtkMenu * menu, int *x, int *y, gboolean * push_in, gpointer user_data) { GtkWidget *w; int screen_width, screen_height; GtkRequisition requisition; w = GTK_WIDGET(user_data); gdk_window_get_origin(w->window, x, y); *x += w->allocation.x; *y += w->allocation.y + w->allocation.height; gtk_widget_size_request(GTK_WIDGET(menu), &requisition); screen_width = gdk_screen_width(); screen_height = gdk_screen_height(); *x = CLAMP(*x, 0, MAX(0, screen_width - requisition.width)); *y = CLAMP(*y, 0, MAX(0, screen_height - requisition.height)); } static gboolean open_button_pressed_cb(GtkWidget * widget, GdkEventButton * event, gpointer * user_data) { GtkWidget *menu; guint button = 0; guint32 activate_time = GDK_CURRENT_TIME; g_return_val_if_fail(GTK_IS_BUTTON(widget), FALSE); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE); menu = g_object_get_data(G_OBJECT(widget), "recent-menu"); if(event != NULL) { button = event->button; activate_time = event->time; } gtk_menu_popup(GTK_MENU(menu), NULL, NULL, menu_position_under_widget, widget, button, activate_time); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), FALSE); return TRUE; } static gboolean open_button_key_pressed_cb(GtkWidget * widget, GdkEventKey * event, gpointer * user_data) { if(event->keyval == GDK_space || event->keyval == GDK_KP_Space || event->keyval == GDK_Return || event->keyval == GDK_KP_Enter) { open_button_pressed_cb(widget, NULL, user_data); } return FALSE; } static gboolean key_press_cb(GtkWidget * widget, GdkEventKey * event, gpointer * data) { GgvWindow *win = GGV_WINDOW(widget); if(event->keyval == GDK_Escape && win->fullscreen) { ggv_window_set_fullscreen(win, FALSE); return TRUE; } else return FALSE; } static gboolean ggv_file_open_recent(EggRecentView * view, EggRecentItem * item, gpointer data) { GtkWidget *newwin; GgvWindow *win = GGV_WINDOW(data); gchar *uri_utf8; uri_utf8 = egg_recent_item_get_uri_utf8(item); if(win->page_count <= 0) newwin = GTK_WIDGET(win); else { newwin = ggv_window_new(); } if(ggv_window_load(GGV_WINDOW(newwin), uri_utf8)) { if(newwin != GTK_WIDGET(win)) gtk_widget_show(newwin); } else { GtkWidget *dlg; dlg = gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Unable to load file:\n%s"), uri_utf8); gtk_widget_show(dlg); gtk_dialog_run(GTK_DIALOG(dlg)); gtk_widget_destroy(dlg); if(newwin != GTK_WIDGET(win)) ggv_window_close(GGV_WINDOW(newwin)); } return TRUE; } void ggv_add_open_button(GgvWindow * win, BonoboUIComponent * ui_component, const gchar * path, const gchar * tooltip) { GtkWidget *menu; EggRecentViewGtk *view; EggRecentModel *model; GtkWidget *button; GtkTooltips *tooltips; tooltips = gtk_tooltips_new(); button = gtk_toggle_button_new(); gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); gtk_tooltips_set_tip(tooltips, button, _("Open recent files"), NULL); gtk_container_add(GTK_CONTAINER(button), gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT)); gtk_widget_show_all(GTK_WIDGET(button)); model = ggv_recent_get_model(); menu = gtk_menu_new(); gtk_widget_show(menu); view = egg_recent_view_gtk_new(menu, NULL); g_signal_connect(view, "activate", G_CALLBACK(ggv_file_open_recent), win); egg_recent_view_gtk_show_icons(view, TRUE); egg_recent_view_gtk_show_numbers(view, FALSE); egg_recent_view_set_model(EGG_RECENT_VIEW(view), model); g_object_set_data(G_OBJECT(button), "recent-menu", menu); g_signal_connect_object(button, "key_press_event", G_CALLBACK(open_button_key_pressed_cb), win, 0); g_signal_connect_object(button, "button_press_event", G_CALLBACK(open_button_pressed_cb), win, 0); bonobo_ui_component_widget_set(ui_component, path, GTK_WIDGET(button), NULL); } GtkWidget * ggv_window_new(void) { GgvWindow *win; BonoboUIContainer *ui_container; BonoboUIComponent *uic, *popup_uic; CORBA_Environment ev; Bonobo_Control control; GtkWidget *widget; EggRecentViewBonobo *view; EggRecentModel *model; Bonobo_PropertyBag pb; gchar *mask; static const GtkTargetEntry drag_types[] = { {"text/uri-list", 0, TARGET_URI_LIST} }; /* get the control */ CORBA_exception_init(&ev); control = bonobo_get_object(GGV_POSTSCRIPT_VIEWER_CONTROL_IID, "Bonobo/Control", &ev); if(BONOBO_EX(&ev) || control == CORBA_OBJECT_NIL) { g_warning("Could not get GGV control: '%s'", bonobo_exception_get_text(&ev)); CORBA_exception_free(&ev); return NULL; } CORBA_exception_free(&ev); win = GGV_WINDOW(g_object_new (GGV_TYPE_WINDOW, "win_name", "ggv", "title", _("GGV: no document loaded"), NULL)); win->control = control; win->current_page = 0; win->show_toolbar = ggv_toolbar; win->show_menus = ggv_menubar; win->show_statusbar = ggv_statusbar; win->show_sidebar = ggv_panel; /* a vbox */ win->vbox = gtk_vbox_new(FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(win->vbox), 2); gtk_widget_show(win->vbox); /* a hbox at its top */ win->hbox = gtk_hbox_new(FALSE, 2); gtk_widget_show(win->hbox); gtk_box_pack_start(GTK_BOX(win->vbox), win->hbox, TRUE, TRUE, 2); /* add statusbar */ win->statusbar = gnome_appbar_new(FALSE, TRUE, GNOME_PREFERENCES_NEVER); if(ggv_statusbar) gtk_widget_show(GTK_WIDGET(win->statusbar)); gtk_box_pack_end(GTK_BOX(win->vbox), GTK_WIDGET(win->statusbar), FALSE, FALSE, 0); bonobo_window_set_contents(BONOBO_WINDOW(win), win->vbox); gtk_drag_dest_set(GTK_WIDGET(win), GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP, drag_types, sizeof(drag_types) / sizeof(drag_types[0]), GDK_ACTION_COPY | GDK_ACTION_ASK); /* add menu and toolbar */ ui_container = bonobo_window_get_ui_container(BONOBO_WINDOW(win)); uic = bonobo_ui_component_new("ggv"); win->uic = uic; bonobo_ui_component_set_container(uic, BONOBO_OBJREF(ui_container), NULL); bonobo_ui_util_set_ui(uic, DATADIR, "ggv-ui.xml", "GGV", NULL); ggv_add_open_button(win, uic, "/Toolbar/TBOpenMenu", _("Open a file.")); bonobo_ui_component_add_verb_list_with_data(uic, ggv_app_verbs, win); bonobo_ui_component_add_listener(uic, "SettingsShowMenus", listener_SettingsShow, win); bonobo_ui_component_add_listener(uic, "SettingsShowSidebar", listener_SettingsShow, win); bonobo_ui_component_add_listener(uic, "SettingsShowToolbar", listener_SettingsShow, win); bonobo_ui_component_add_listener(uic, "SettingsShowStatusbar", listener_SettingsShow, win); bonobo_ui_component_add_listener(uic, "ViewFullscreen", listener_ViewFullscreen, win); sync_settings_menu_items(win); model = ggv_recent_get_model(); view = egg_recent_view_bonobo_new(uic, "/menu/File/Recents"); egg_recent_view_bonobo_show_icons(EGG_RECENT_VIEW_BONOBO(view), FALSE); egg_recent_view_set_model(EGG_RECENT_VIEW(view), model); g_signal_connect(G_OBJECT(view), "activate", G_CALLBACK(ggv_file_open_recent), win); g_signal_connect(G_OBJECT(win), "key_press_event", G_CALLBACK(key_press_cb), NULL); if(!win->show_toolbar) bonobo_ui_component_set_prop(uic, "/Toolbar", "hidden", "1", NULL); if(!win->show_menus) bonobo_ui_component_set_prop(uic, "/menu", "hidden", "1", NULL); /* add control frame interface and bind it to ggv control */ win->ctlframe = bonobo_control_frame_new(BONOBO_OBJREF(ui_container)); bonobo_control_frame_set_autoactivate(win->ctlframe, FALSE); g_signal_connect(G_OBJECT(win->ctlframe), "activate_uri", (GtkSignalFunc) control_frame_activate_uri, NULL); bonobo_control_frame_bind_to_control(win->ctlframe, control, NULL); widget = bonobo_control_frame_get_widget(win->ctlframe); gtk_widget_show(widget); gtk_box_pack_start(GTK_BOX(win->hbox), widget, TRUE, TRUE, 0); bonobo_control_frame_control_activate(win->ctlframe); /* now get the control's property bag */ pb = bonobo_control_frame_get_control_property_bag(win->ctlframe, NULL); if(pb == CORBA_OBJECT_NIL) { g_warning("Control does not have any properties."); } else { /* TODO: set initial status & title */ mask = "Bonobo/Property:change:page," "Bonobo/Property:change:page_count," "Bonobo/Property:change:width," "Bonobo/Property:change:height," "Bonobo/Property:change:title," "Bonobo/Property:change:status"; bonobo_event_source_client_add_listener(pb, (BonoboListenerCallbackFn) control_property_changed_handler, mask, NULL, win); win->pb = pb; } /* merge our items in the control's popup menu */ popup_uic = bonobo_control_frame_get_popup_component(win->ctlframe, NULL); if(popup_uic == NULL) { g_warning("Control does not have a popup component."); } else { bonobo_ui_util_set_ui(popup_uic, DATADIR, "ggv-ui.xml", "GGV", NULL); bonobo_ui_component_add_listener(popup_uic, "SettingsShowMenus", listener_SettingsShow, win); bonobo_ui_component_add_listener(popup_uic, "SettingsShowSidebar", listener_SettingsShow, win); bonobo_ui_component_add_listener(popup_uic, "SettingsShowToolbar", listener_SettingsShow, win); bonobo_ui_component_add_listener(popup_uic, "SettingsShowStatusbar", listener_SettingsShow, win); bonobo_ui_component_add_listener(popup_uic, "ViewFullscreen", listener_ViewFullscreen, win); win->popup_uic = popup_uic; sync_settings_popup_items(win); } /* set default geometry */ gtk_widget_set_usize(GTK_WIDGET(win), ggv_default_width, ggv_default_height); gtk_window_set_policy(GTK_WINDOW(win), TRUE, TRUE, FALSE); gconf_client_notify_add(ggv_prefs_gconf_client(), "/apps/ggv/layout", (GConfClientNotifyFunc) prefs_changed, win, NULL, NULL); set_file_cmds_sensitivity(win, FALSE); return GTK_WIDGET(win); } gboolean ggv_window_load(GgvWindow * win, const gchar * filename) { CORBA_Environment ev; Bonobo_PersistFile pf; gchar *title, *unescaped, *utf8_title, *_filename; Bonobo_UIContainer container; gint i; gchar *path_end; g_return_val_if_fail(win != NULL, FALSE); g_return_val_if_fail(GGV_IS_WINDOW(win), FALSE); g_return_val_if_fail(filename != NULL, FALSE); ggv_window_set_watch_file(win, FALSE); CORBA_exception_init(&ev); pf = Bonobo_Unknown_queryInterface(win->control, "IDL:Bonobo/PersistFile:1.0", &ev); if(BONOBO_EX(&ev) || pf == CORBA_OBJECT_NIL) { CORBA_exception_free(&ev); ggv_window_set_watch_file(win, ggv_watch_doc); return FALSE; } Bonobo_PersistFile_load(pf, filename, &ev); bonobo_object_release_unref(pf, NULL); _filename = g_strdup(filename); if(win->filename) g_free(win->filename); win->filename = _filename; if(BONOBO_EX(&ev)) { CORBA_exception_free(&ev); ggv_window_set_watch_file(win, ggv_watch_doc); return FALSE; } CORBA_exception_free(&ev); win->page_count = bonobo_pbclient_get_long(win->pb, "page_count", NULL); container = bonobo_ui_component_get_container(win->uic); if(container != CORBA_OBJECT_NIL) bonobo_ui_component_set_prop (win->uic, "/commands/FileReload", "sensitive", (win->page_count > 0) ? "1" : "0", NULL); ggv_window_set_watch_file(win, ggv_watch_doc); for(i = strlen(win->filename) - 1; (i >= 0) && (win->filename[i] != '/'); i--) ; if(win->filename[i] == '/') path_end = &win->filename[i + 1]; else path_end = win->filename; unescaped = gnome_vfs_unescape_string_for_display(path_end); title = g_strconcat(unescaped, _(" - GGV"), NULL); g_free(unescaped); utf8_title = g_locale_to_utf8(title, -1, NULL, NULL, NULL); g_free(title); gtk_window_set_title(GTK_WINDOW(win), utf8_title); g_free(utf8_title); return TRUE; } void ggv_window_close(GgvWindow * win) { g_return_if_fail(win != NULL); g_return_if_fail(GGV_IS_WINDOW(win)); bonobo_control_frame_control_deactivate(win->ctlframe); gtk_widget_destroy(GTK_WIDGET(win)); if(!window_list) bonobo_main_quit(); } const GList * ggv_get_window_list() { return window_list; } gboolean ggv_window_reload(GgvWindow * win) { CORBA_Environment ev; Bonobo_PersistFile pf; Bonobo_UIContainer container; gint page = win->current_page; CORBA_exception_init(&ev); pf = Bonobo_Unknown_queryInterface(win->control, "IDL:Bonobo/PersistFile:1.0", &ev); if(BONOBO_EX(&ev) || pf == CORBA_OBJECT_NIL) { CORBA_exception_free(&ev); return FALSE; } Bonobo_PersistFile_load(pf, win->filename, &ev); bonobo_object_release_unref(pf, NULL); if(BONOBO_EX(&ev)) { CORBA_exception_free(&ev); return FALSE; } CORBA_exception_free(&ev); win->page_count = bonobo_pbclient_get_long(win->pb, "page_count", NULL); bonobo_pbclient_set_long(win->pb, "page", page, NULL); container = bonobo_ui_component_get_container(win->uic); if(container != CORBA_OBJECT_NIL) bonobo_ui_component_set_prop (win->uic, "/commands/FileReload", "sensitive", (win->page_count > 0) ? "1" : "0", NULL); return TRUE; } /* Reload file if it has changed */ static void monitor_callback(GnomeVFSMonitorHandle *handle, const gchar *monitor_uri, const gchar *info_uri, GnomeVFSMonitorEventType event_type, gpointer user_data) { GgvWindow *win = GGV_WINDOW(user_data); if(event_type != GNOME_VFS_MONITOR_EVENT_CHANGED && event_type != GNOME_VFS_MONITOR_EVENT_DELETED && event_type != GNOME_VFS_MONITOR_EVENT_CREATED) return; /* perform a short sleep, just in case if the file "just started to be * changed"; naturally, this method is not 100% accurate, but suffices * for most of the cases... */ usleep(250000); if(!ggv_window_reload(win)) { GtkWidget *dlg; dlg = gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Unable to reload file after it changed:\n%s"), win->filename); gtk_widget_show(dlg); gtk_dialog_run(GTK_DIALOG(dlg)); gtk_widget_destroy(dlg); } } void ggv_window_set_watch_file(GgvWindow * win, gboolean f) { GnomeVFSResult res; if(win->watch_doc == f) return; win->watch_doc = f; if(win->watch_doc) { if(NULL != win->monitor) { gnome_vfs_monitor_cancel(win->monitor); win->monitor = NULL; } if(NULL != win->filename) { if(GNOME_VFS_OK != (res = gnome_vfs_monitor_add(&(win->monitor), win->filename, GNOME_VFS_MONITOR_FILE, monitor_callback, win))) { win->monitor = NULL; } } } else { if(NULL != win->monitor) { gnome_vfs_monitor_cancel(win->monitor); win->monitor = NULL; } } }