#ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include "gm-debug.h" #include "gm-app-view.h" #include "gm-ui.h" #include "gm-world-view.h" #include "gm-world.h" #include "dialogs/gm-worlds-list-dialog.h" #include "dialogs/gm-open-world-dialog.h" #include "dialogs/gm-world-logs-dialog.h" #include "dialogs/gm-world-properties-dialog.h" #include "dialogs/gm-scripts-dialog.h" #include "dialogs/gm-preferences-dialog.h" #include "gm-world-tab.h" #include "gm-editor-view.h" #include "mcp/gm-mcp-session.h" #include "mcp/gm-mcp-package.h" #include "gm-searchable.h" #include "gm-tray.h" #include "gm-pixbuf.h" #include "gm-support.h" #include "gm-marshal.h" void on_gm_app_view_entry_find_activate(GtkEntry *entry, GmAppView *view); void on_gm_app_view_entry_find_changed(GtkEditable *editable, GmAppView *view); gboolean on_gm_app_view_entry_find_key_press(GtkWidget *widget, GdkEventKey *event, GmAppView *view); void on_gm_app_view_button_find_close_clicked(GtkButton *button, GmAppView *view); void on_gm_app_view_button_replace_clicked(GtkButton *button, GmAppView *view); void on_gm_app_view_button_replace_all_clicked(GtkButton *button, GmAppView *view); void on_gm_app_view_expander_replace(GObject *object, GParamSpec *param_spec, GmAppView *view); void on_gm_app_view_notebook_switch_page(GtkNotebook * notebook, GtkNotebookPage * page, guint page_num, GmAppView *view); void on_gm_app_view_world_view_notebook_switch_page(GtkNotebook * notebook, GtkNotebookPage * page, guint page_num, GmAppView *view); gboolean on_gm_app_view_notebook_button_press(GtkNotebook *notebook, GdkEventButton *event, GmAppView *view); gboolean on_gm_app_view_notebook_button_release(GtkNotebook *notebook, GdkEventButton *event, GmAppView *view); void on_gm_app_view_check_button_search_direction_toggled( GtkToggleButton *button, GmAppView *view); void on_gm_app_view_check_button_search_sensitive_toggled( GtkToggleButton *button, GmAppView *view); gboolean on_gm_app_view_tray_button_press(GmTray *tray, GdkEventButton *event, GmAppView *view); void on_gm_app_view_tray_message_clicked(GmTray *tray, GmAppView *view); void on_gm_app_view_tray_destroy(GtkWidget *caller, GmAppView *view); void on_gm_app_view_world_mcp_package_created(GmMcpSession *session, GmMcpPackage *package, GmAppView *view); typedef struct _AccelInfo AccelInfo; struct _AccelInfo { int num; GmAppView *view; }; void on_gm_app_view_accel_switch_page(GtkAccelGroup * accelgroup, GObject * arg1, guint arg2, GdkModifierType arg3, AccelInfo *info); void on_gm_app_view_accel_switch_edit(GtkAccelGroup * accelgroup, GObject * arg1, guint arg2, GdkModifierType arg3, AccelInfo *info); void on_gm_app_view_accel_cycle_page(GtkAccelGroup * accelgroup, GObject * arg1, guint arg2, GdkModifierType arg3, AccelInfo *info); void on_gm_app_view_world_added(GmApp *app, GmWorld *world, GmAppView *view); void on_gm_app_view_world_removed(GmApp *app, GmWorld *world, GmAppView *view); void on_gm_app_view_world_activate(GtkAction * action, GmWorld *world); void on_gm_app_view_world_load(GmWorld *world, GmAppView *view); void on_gm_app_view_world_unload(GmWorld *world, GmAppView *view); void on_gm_app_view_world_activate_request(GmWorld *world, GmAppView *view); void on_gm_app_view_world_name_changed(GmWorld *world, GParamSpec *pspec, GmAppView *view); void on_gm_app_view_world_active_changed(GmWorld *world, GParamSpec *pspec, GmAppView *view); void on_gm_app_view_world_activity_changed(GmWorld *world, GParamSpec *pspec, GmAppView *view); void on_gm_app_view_world_state_changing(GmWorld *world, GmNetState state, GmAppView *view); void on_gm_app_view_world_notify_message(GmWorld *world, gchar const *message, GmAppView *view); void on_gm_app_view_world_view_destroy(GmWorldView *world_view, GmAppView *view); #define GM_APP_VIEW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), \ GM_TYPE_APP_VIEW, GmAppViewPrivate)) typedef struct _GmAppViewWorldMenuItem GmAppViewWorldMenuItem; struct _GmAppViewWorldMenuItem { guint merge_id; GtkAction *action; }; void gm_app_view_destroy_world_menu_item(GmAppViewWorldMenuItem *item); struct _GmAppViewPrivate { GmApp *application; GtkUIManager *manager; GtkActionGroup *sensitive_action_group; GtkActionGroup *action_group; GtkActionGroup *worlds_action_group; GtkActionGroup *editor_action_group; GHashTable *world_menu_items; GmTray *tray; GtkNotebook *notebook; GmWorld *active_world; GtkMenuBar *menu; GtkToolbar *toolbar; GtkLabel *label_replace; GtkExpander *expander_replace; GtkEntry *entry_find; GtkEntry *entry_replace; GtkVBox *vbox_find; GtkHBox *hbox_control_find; GtkHBox *hbox_replace; GtkCheckButton *check_button_search_direction; GtkCheckButton *check_button_search_sensitive; gboolean ignore_toggling; gboolean drag_in_progress; gint motion_notify_handler_id; gint x_start; gint y_start; gint tab; GdkCursor *cursor; guint tray_idle_create; gint position_left; gint position_top; }; /* Signals */ enum { ACTIVE_WORLD_CHANGED, NUM_SIGNALS }; static guint gm_app_view_signals[NUM_SIGNALS] = {0}; G_DEFINE_TYPE(GmAppView, gm_app_view, GTK_TYPE_WINDOW) static void gm_app_view_finalize(GObject *object) { GmAppView *view = GM_APP_VIEW(object); #ifdef HAVE_RUBY gm_scripts_dialog_fini(); #endif if (view->priv->cursor) { gdk_cursor_unref(view->priv->cursor); } if (view->priv->tray_idle_create) { g_source_remove(view->priv->tray_idle_create); } if (view->priv->tray) { g_signal_handlers_disconnect_by_func(view->priv->tray, on_gm_app_view_tray_destroy, view); gtk_widget_destroy(GTK_WIDGET(view->priv->tray)); g_object_unref(view->priv->tray); } g_hash_table_destroy(view->priv->world_menu_items); G_OBJECT_CLASS(gm_app_view_parent_class)->finalize(object); } static gboolean gm_app_view_delete_event(GtkWidget *widget, GdkEventAny *event) { gboolean ret = FALSE; if (GTK_WIDGET_CLASS(gm_app_view_parent_class)->delete_event) { ret = GTK_WIDGET_CLASS(gm_app_view_parent_class)->delete_event(widget, event); } gtk_main_quit(); return ret; } static gboolean gm_app_view_focus_in_event(GtkWidget *widget, GdkEventFocus *event) { GmAppView *view = GM_APP_VIEW(widget); GmWorld *world; world = gm_app_view_active_world(view); if (world) { gm_world_set_active(world, TRUE); } if (view->priv->tray) { gm_tray_normal(view->priv->tray); } gtk_window_set_urgency_hint(GTK_WINDOW(view), FALSE); if (GTK_WIDGET_CLASS(gm_app_view_parent_class)->focus_in_event) { return GTK_WIDGET_CLASS(gm_app_view_parent_class)->focus_in_event( widget, event); } return FALSE; } static gboolean gm_app_view_focus_out_event(GtkWidget *widget, GdkEventFocus *event) { GmAppView *view = GM_APP_VIEW(widget); GmWorld *world; world = gm_app_view_active_world(view); if (world) { // Set the world to inactive so activity is counted and tray will // update properly gm_world_set_active(world, FALSE); } if (GTK_WIDGET_CLASS(gm_app_view_parent_class)->focus_out_event) { return GTK_WIDGET_CLASS(gm_app_view_parent_class)->focus_out_event( widget, event); } return FALSE; } static void gm_app_view_size_allocate(GtkWidget *widget, GtkAllocation *allocation) { GmAppView *view = GM_APP_VIEW(widget); gm_options_set_int(gm_app_options(view->priv->application), "width", allocation->width); gm_options_set_int(gm_app_options(view->priv->application), "height", allocation->height); if (GTK_WIDGET_CLASS(gm_app_view_parent_class)->size_allocate) { GTK_WIDGET_CLASS(gm_app_view_parent_class)->size_allocate(widget, allocation); } } static gboolean gm_app_view_window_state_event(GtkWidget *widget, GdkEventWindowState *event) { GmAppView *view = GM_APP_VIEW(widget); if (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED && GTK_WIDGET_VISIBLE(widget)) { gm_options_set_int(gm_app_options(view->priv->application), "maximized", (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0); } if (GTK_WIDGET_CLASS(gm_app_view_parent_class)->window_state_event) { return GTK_WIDGET_CLASS(gm_app_view_parent_class)->window_state_event( widget, event); } return FALSE; } static void gm_app_view_class_init(GmAppViewClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); object_class->finalize = gm_app_view_finalize; widget_class->delete_event = gm_app_view_delete_event; widget_class->focus_in_event = gm_app_view_focus_in_event; widget_class->focus_out_event = gm_app_view_focus_out_event; widget_class->size_allocate = gm_app_view_size_allocate; widget_class->window_state_event = gm_app_view_window_state_event; gm_app_view_signals[ACTIVE_WORLD_CHANGED] = g_signal_new("active_world_changed", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GmAppViewClass, active_world_changed), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); g_type_class_add_private(object_class, sizeof(GmAppViewPrivate)); } void gm_app_view_create_ui(GmAppView *view) { GError *error = NULL; GtkActionGroup *action_group; GtkAction *action; #ifdef HAVE_PARSER guint merge_id; #endif view->priv->manager = gtk_ui_manager_new(); gtk_window_add_accel_group(GTK_WINDOW(view), gtk_ui_manager_get_accel_group(view->priv->manager)); gtk_ui_manager_add_ui_from_file(view->priv->manager, PACKAGE_DATA_DIR "/" PACKAGE "/ui/gm-ui.xml", &error); if (error) { gm_debug_msg(DEBUG_ALWAYS, "Could not merge UI file"); g_error_free(error); g_object_unref(view->priv->manager); view->priv->manager = NULL; return; } /* World sensitive actions */ action_group = gtk_action_group_new("GmAppViewSensitiveActions"); gtk_action_group_set_translation_domain(action_group, GETTEXT_PACKAGE); gtk_action_group_add_actions(action_group, gm_sensitive_menu_entries, G_N_ELEMENTS(gm_sensitive_menu_entries), view); gtk_ui_manager_insert_action_group(view->priv->manager, action_group, 0); view->priv->sensitive_action_group = action_group; gtk_action_group_set_sensitive(action_group, TRUE); /* World insensitive actions */ action_group = gtk_action_group_new("GmAppViewActions"); gtk_action_group_set_translation_domain(action_group, GETTEXT_PACKAGE); gtk_action_group_add_actions(action_group, gm_menu_entries, G_N_ELEMENTS(gm_menu_entries), view); gtk_ui_manager_insert_action_group(view->priv->manager, action_group, 0); view->priv->action_group = action_group; gtk_action_group_set_sensitive(action_group, FALSE); /* Worlds list action group */ view->priv->worlds_action_group = gtk_action_group_new("GmAppViewWorldsActions"); gtk_action_group_set_translation_domain(view->priv->worlds_action_group, GETTEXT_PACKAGE); gtk_ui_manager_insert_action_group(view->priv->manager, view->priv->worlds_action_group, 0); /* Editor action group */ view->priv->editor_action_group = gtk_action_group_new("GmAppViewEditorActions"); gtk_action_group_set_translation_domain(view->priv->editor_action_group, GETTEXT_PACKAGE); gtk_action_group_add_actions(view->priv->editor_action_group, gm_editor_entries, G_N_ELEMENTS(gm_editor_entries), view); gtk_ui_manager_insert_action_group(view->priv->manager, view->priv->editor_action_group, 0); gtk_action_group_set_visible(view->priv->editor_action_group, FALSE); gtk_action_group_set_sensitive(view->priv->editor_action_group, FALSE); #ifndef HAVE_RUBY gtk_action_set_sensitive(gm_app_view_action(view, "/MenuBar/EditMenu/EditScriptsMenu"), FALSE); #endif #ifdef HAVE_PARSER merge_id = gtk_ui_manager_new_merge_id(view->priv->manager); gtk_ui_manager_add_ui(view->priv->manager, merge_id, "/MenuBar/EditorMenu/EditorMenuAdditions", "EditorParseMenu", "EditorParse", GTK_UI_MANAGER_MENUITEM, FALSE); merge_id = gtk_ui_manager_new_merge_id(view->priv->manager); gtk_ui_manager_add_ui(view->priv->manager, merge_id, "/ToolBar/EditorToolAdditions", "EditorParseTool", "EditorParse", GTK_UI_MANAGER_TOOLITEM, FALSE); #endif action = gm_app_view_action(view, "/MenuBar/WorldMenu/WorldOpenMenu"); g_object_set(action, "short-label", _("Open"), NULL); } GtkWidget * create_button(gchar *label, gchar *stock) { GtkWidget *align = gtk_alignment_new(0.5, 0.5, 0.0, 0.0); GtkWidget *button = gtk_button_new(); GtkWidget *hbox = gtk_hbox_new(FALSE, 2); gtk_container_add(GTK_CONTAINER(button), align); gtk_container_add(GTK_CONTAINER(align), hbox); gtk_box_pack_start(GTK_BOX(hbox), gtk_image_new_from_stock( stock, GTK_ICON_SIZE_BUTTON), FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(label), FALSE, FALSE, 0); return button; } GtkWidget * gm_app_view_create_search_box(GmAppView *view) { GtkWidget *vbox = gtk_vbox_new(FALSE, 3); GtkWidget *table = gtk_table_new(2, 2, FALSE); GtkWidget *hbox; GtkWidget *hbox_control = gtk_hbox_new(FALSE, 6); GtkWidget *lbl; GtkWidget *entry; GtkWidget *button; GtkWidget *align; GtkWidget *check_button; GtkWidget *button_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE); GtkWidget *separator = gtk_hseparator_new(); GtkWidget *expander = gtk_expander_new(NULL); gtk_container_set_border_width(GTK_CONTAINER(table), 3); hbox = gtk_hbox_new(FALSE, 6); lbl = gtk_label_new(_("Find:")); gtk_misc_set_alignment(GTK_MISC(lbl), 1.0, 0.5); gtk_widget_set_sensitive(expander, FALSE); align = gtk_alignment_new(0.0, 0.5, 0.0, 0.0); gtk_container_add(GTK_CONTAINER(align), expander); g_signal_connect(expander, "notify::expanded", G_CALLBACK(on_gm_app_view_expander_replace), view); gtk_box_pack_start(GTK_BOX(hbox), align, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), lbl, FALSE, FALSE, 0); gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, 0, 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0.0, 0.0); lbl = gtk_label_new(_("Replace:")); view->priv->label_replace = GTK_LABEL(lbl); gtk_misc_set_alignment(GTK_MISC(lbl), 1.0, 0.5); gtk_table_attach(GTK_TABLE(table), lbl, 0, 1, 1, 2, GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0.0, 0.0); hbox = gtk_hbox_new(FALSE, 6); gtk_container_set_border_width(GTK_CONTAINER(hbox), 3); gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0.0, 0.0); gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), separator, FALSE, TRUE, 0); entry = gtk_entry_new(); view->priv->entry_find = GTK_ENTRY(entry); button = create_button(_("Find next"), GTK_STOCK_FIND); g_signal_connect(button, "clicked", G_CALLBACK(on_gm_app_view_entry_find_activate), view); g_signal_connect(entry, "key_press_event", G_CALLBACK(on_gm_app_view_entry_find_key_press), view); g_signal_connect(entry, "activate", G_CALLBACK(on_gm_app_view_entry_find_activate), view); g_signal_connect(entry, "changed", G_CALLBACK(on_gm_app_view_entry_find_changed), view); gtk_box_pack_start(GTK_BOX(hbox_control), entry, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox_control), button, FALSE, FALSE, 0); check_button = gtk_check_button_new_with_label(_("Search backwards")); g_signal_connect(check_button, "toggled", G_CALLBACK(on_gm_app_view_check_button_search_direction_toggled), view); view->priv->check_button_search_direction = GTK_CHECK_BUTTON(check_button); gtk_box_pack_start(GTK_BOX(hbox_control), check_button, FALSE, FALSE, 0); check_button = gtk_check_button_new_with_label(_("Match case")); g_signal_connect(check_button, "toggled", G_CALLBACK(on_gm_app_view_check_button_search_sensitive_toggled), view); view->priv->check_button_search_sensitive = GTK_CHECK_BUTTON(check_button); gtk_box_pack_start(GTK_BOX(hbox_control), check_button, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), hbox_control, FALSE, FALSE, 0); gtk_box_pack_end(GTK_BOX(hbox), button_close, FALSE, FALSE, 0); hbox = gtk_hbox_new(FALSE, 6); gtk_container_set_border_width(GTK_CONTAINER(hbox), 3); entry = gtk_entry_new(); g_signal_connect(entry, "key_press_event", G_CALLBACK(on_gm_app_view_entry_find_key_press), view); view->priv->entry_replace = GTK_ENTRY(entry); gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0); button = create_button(_("Replace"), GTK_STOCK_FIND_AND_REPLACE); g_signal_connect(button, "clicked", G_CALLBACK(on_gm_app_view_button_replace_clicked), view); gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); button = gtk_button_new_with_label(_("Replace all")); g_signal_connect(button, "clicked", G_CALLBACK(on_gm_app_view_button_replace_all_clicked), view); gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0.0, 0.0); view->priv->expander_replace = GTK_EXPANDER(expander); view->priv->hbox_replace = GTK_HBOX(hbox); view->priv->vbox_find = GTK_VBOX(vbox); view->priv->hbox_control_find = GTK_HBOX(hbox_control); view->priv->ignore_toggling = FALSE; gtk_widget_show_all(vbox); gtk_widget_hide(GTK_WIDGET(view->priv->label_replace)); gtk_widget_hide(GTK_WIDGET(view->priv->hbox_replace)); gtk_widget_hide(vbox); g_signal_connect(button_close, "clicked", G_CALLBACK(on_gm_app_view_button_find_close_clicked), view); return vbox; } void gm_app_view_create_keybindings(GmAppView *view) { GtkAccelGroup *grp; GClosure *closure; int i; gchar num[2] = { 0, 0 }; AccelInfo *info; grp = gtk_accel_group_new(); // Setting up Alt-1/Ctrl-1 -> Alt-9/Ctrl-9 accelerators for (i = 0; i < 9; i++) { info = g_new0(AccelInfo, 1); info->num = i; info->view = view; num[0] = '1' + i; closure = g_cclosure_new(G_CALLBACK(on_gm_app_view_accel_switch_page), info, NULL); gtk_accel_group_connect(grp, gdk_keyval_from_name(num), GDK_MOD1_MASK, GTK_ACCEL_VISIBLE, closure); g_closure_unref(closure); closure = g_cclosure_new(G_CALLBACK(on_gm_app_view_accel_switch_edit), info, NULL); gtk_accel_group_connect(grp, gdk_keyval_from_name(num), GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE, closure); g_closure_unref(closure); } info = g_new0(AccelInfo, 1); info->num = 1; info->view = view; // Next page closure = g_cclosure_new(G_CALLBACK(on_gm_app_view_accel_cycle_page), info, NULL); gtk_accel_group_connect(grp, GDK_Page_Up, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE, closure); g_closure_unref(closure); info = g_new0(AccelInfo, 1); info->num = -1; info->view = view; // Previous page closure = g_cclosure_new(G_CALLBACK(on_gm_app_view_accel_cycle_page), info, NULL); gtk_accel_group_connect(grp, GDK_Page_Down, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE, closure); g_closure_unref(closure); gtk_window_add_accel_group(GTK_WINDOW(view), grp); } static void gm_app_view_create_tray(GmAppView *view) { view->priv->tray = g_object_ref(gm_tray_new(_("GnoeMoe Gnome MOO Client"))); gm_tray_set_icon(view->priv->tray, TRAY_STATE_NORMAL, gm_pixbuf_get("tray/default.svg")); gm_tray_set_icon(view->priv->tray, TRAY_STATE_ACTIVE, gm_pixbuf_get("tray/active.svg")); gm_tray_set_icon(view->priv->tray, TRAY_STATE_NOTIFY, gm_pixbuf_get("tray/notify.svg")); gm_tray_set_icon(view->priv->tray, TRAY_STATE_ACTIVITY, gm_pixbuf_get("tray/activity.svg")); gtk_widget_show(GTK_WIDGET(view->priv->tray)); g_signal_connect(view->priv->tray, "message-clicked", G_CALLBACK(on_gm_app_view_tray_message_clicked), view); g_signal_connect(view->priv->tray, "button-press-event", G_CALLBACK(on_gm_app_view_tray_button_press), view); g_signal_connect(view->priv->tray, "destroy", G_CALLBACK(on_gm_app_view_tray_destroy), view); } static void gm_app_view_init(GmAppView *view) { GtkWidget *menu = NULL; GtkWidget *toolbar = NULL; GtkWidget *search; GtkWidget *vbox; GtkWidget *note; view->priv = GM_APP_VIEW_GET_PRIVATE(view); vbox = gtk_vbox_new(FALSE, 3); gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(view), vbox); gm_app_view_create_ui(view); if (view->priv->manager) { menu = gtk_ui_manager_get_widget(view->priv->manager, "/MenuBar"); if (menu) { gtk_widget_show(menu); gtk_box_pack_start(GTK_BOX(vbox), menu, FALSE, TRUE, 0); } toolbar = gtk_ui_manager_get_widget(view->priv->manager, "/ToolBar"); if (toolbar) { gtk_widget_show(toolbar); gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, TRUE, 0); } } search = gm_app_view_create_search_box(view); gtk_box_pack_start(GTK_BOX(vbox), search, FALSE, TRUE, 0); note = gtk_notebook_new(); gtk_widget_show(note); gtk_container_set_border_width(GTK_CONTAINER(note), 3); gtk_notebook_set_show_border(GTK_NOTEBOOK(note), FALSE); GTK_WIDGET_UNSET_FLAGS(note, GTK_CAN_FOCUS); gtk_widget_add_events(GTK_WIDGET(note), GDK_BUTTON1_MOTION_MASK); gtk_box_pack_start(GTK_BOX(vbox), note, TRUE, TRUE, 0); gm_app_view_create_keybindings(view); view->priv->menu = GTK_MENU_BAR(menu); view->priv->toolbar = GTK_TOOLBAR(toolbar); view->priv->notebook = GTK_NOTEBOOK(note); gtk_window_set_title(GTK_WINDOW(view), "GnoeMoe"); gtk_window_set_icon(GTK_WINDOW(view), gm_pixbuf_get("gnoemoe_logo.svg")); /* Signals for tab reordering */ g_signal_connect_after(note, "switch-page", G_CALLBACK(on_gm_app_view_notebook_switch_page), view); g_signal_connect(note, "button-press-event", G_CALLBACK(on_gm_app_view_notebook_button_press), view); g_signal_connect(note, "button-release-event", G_CALLBACK(on_gm_app_view_notebook_button_release), view); view->priv->world_menu_items = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)gm_app_view_destroy_world_menu_item); view->priv->drag_in_progress = FALSE; view->priv->motion_notify_handler_id = 0; view->priv->x_start = 0; view->priv->y_start = 0; view->priv->cursor = NULL; #ifdef HAVE_RUBY gm_scripts_dialog_init(); #endif gm_app_view_create_tray(view); } static void gm_app_view_store_position(GmAppView *view) { gtk_window_get_position(GTK_WINDOW(view), &(view->priv->position_left), &(view->priv->position_top)); } static void gm_app_view_restore_position(GmAppView *view) { gtk_window_move(GTK_WINDOW(view), view->priv->position_left, view->priv->position_top); } static void gm_app_view_restore_size(GmAppView *view) { gint width, height; width = gm_options_get_int(gm_app_options(view->priv->application), "width"); height = gm_options_get_int(gm_app_options(view->priv->application), "height"); if (height > 10 && width > 10) { gtk_window_set_default_size(GTK_WINDOW(view), width, height); } if (gm_options_get_int(gm_app_options(view->priv->application), "maximized")) { gtk_window_maximize(GTK_WINDOW(view)); } } // Public functions GmAppView * gm_app_view_new(GmApp *application) { GmAppView *view = GM_APP_VIEW(g_object_new(GM_TYPE_APP_VIEW, NULL)); view->priv->application = application; view->priv->active_world = NULL; // Set stored width and height gm_app_view_restore_size(view); // Toggle search direction gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON( view->priv->check_button_search_direction), gm_options_get_int(gm_app_options(application), "search_direction_world")); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON( view->priv->check_button_search_sensitive), gm_options_get_int(gm_app_options(application), "search_sensitive")); g_signal_connect(application, "world_added", G_CALLBACK(on_gm_app_view_world_added), view); g_signal_connect(application, "world_removed", G_CALLBACK(on_gm_app_view_world_removed), view); return view; } GmApp const * gm_app_view_application(GmAppView *view) { return view->priv->application; } GtkNotebook * gm_app_view_notebook(GmAppView *view) { return view->priv->notebook; } void gm_app_view_destroy_world_menu_item(GmAppViewWorldMenuItem *item) { g_object_unref(item->action); g_free(item); } void on_gm_app_view_world_removed(GmApp *app, GmWorld *world, GmAppView *view) { GmAppViewWorldMenuItem *item = (GmAppViewWorldMenuItem *) g_hash_table_lookup(view->priv->world_menu_items, world); gtk_ui_manager_remove_ui(view->priv->manager, item->merge_id); gtk_action_group_remove_action(view->priv->worlds_action_group, item->action); g_hash_table_remove(view->priv->world_menu_items, world); } void gm_app_view_set_sensitivity(GmAppView *view, gboolean sens) { gtk_action_group_set_sensitive(view->priv->action_group, sens); if (sens == TRUE) { gtk_action_set_visible(gtk_action_group_get_action( view->priv->action_group, "WorldInfo"), FALSE); } if (!sens) { gtk_widget_hide(GTK_WIDGET(view->priv->vbox_find)); } } GmWorldView * gm_app_view_active_world_view(GmAppView *view) { gint n; n = gtk_notebook_get_current_page(view->priv->notebook); if (n == -1) { return NULL; } return GM_WORLD_VIEW(gtk_notebook_get_nth_page(view->priv->notebook, n)); } GmWorld * gm_app_view_active_world(GmAppView *view) { GmWorldView *world_view = gm_app_view_active_world_view(view); if (world_view != NULL) { return gm_world_view_world(world_view); } else { return NULL; } } void gm_app_view_update_title(GmAppView *view) { gchar *title; GmWorld *world = gm_app_view_active_world(view); if (world == NULL) { gtk_window_set_title(GTK_WINDOW(view), "GnoeMoe"); } else { title = g_strconcat(gm_world_name(world), " - GnoeMoe", NULL); gtk_window_set_title(GTK_WINDOW(view), title); g_free(title); } } GtkMenuItem * gm_app_view_menu_item(GmAppView *view, gchar const *path) { GtkMenuItem *img = GTK_MENU_ITEM( gtk_ui_manager_get_widget(view->priv->manager, path)); return img; } GtkAction * gm_app_view_action(GmAppView *view, gchar const *path) { GtkAction *action = gtk_ui_manager_get_action(view->priv->manager, path); return action; } void gm_app_view_update_connect_button(GmAppView *view, gboolean connected) { GtkAction *action = gm_app_view_action(view, "/MenuBar/WorldMenu/WorldConnectMenu"); if (!connected) { g_object_set(action, "stock-id", GTK_STOCK_CONNECT, NULL); g_object_set(action, "label", _("Connect"), NULL); g_object_set(action, "tooltip", _("Connect the current world"), NULL); } else { g_object_set(action, "stock-id", GTK_STOCK_DISCONNECT, NULL); g_object_set(action, "label", _("Disconnect"), NULL); g_object_set(action, "tooltip", _("Disconnect the current world"), NULL); } } void gm_app_view_worlds_unloaded(GmAppView *view) { gm_app_view_set_sensitivity(view, FALSE); gm_app_view_update_connect_button(view, FALSE); gm_app_view_update_title(view); } void gm_app_view_worlds_loaded(GmAppView *view) { gm_app_view_set_sensitivity(view, TRUE); gtk_action_set_sensitive(gm_app_view_action(view, "/MenuBar/EditMenu/EditReplaceMenu"), FALSE); } void gm_app_view_update_tray(GmAppView *view) { GList *worlds; GmWorld *world; gchar *tmp; gboolean active = gtk_window_is_active(GTK_WINDOW(view)); gint activity; GString *str = NULL; if (!view->priv->tray) { return; } if (!active) { for (worlds = gm_app_worlds(view->priv->application); worlds; worlds = worlds->next) { world = GM_WORLD(worlds->data); activity = gm_world_activity(world); if (gm_world_loaded(world) && activity) { if (!str) { tmp = g_strdup_printf(_("Activity in:\n%s (%d)"), gm_world_name(world), activity); str = g_string_new(NULL); } else { tmp = g_strdup_printf(", %s (%d)", gm_world_name(world), activity); } str = g_string_append(str, tmp); g_free(tmp); } } if (str) { gm_tray_activate(view->priv->tray); } else if (gm_tray_get_state(view->priv->tray) != TRAY_STATE_NOTIFY) { gm_tray_normal(view->priv->tray); } if (gm_tray_get_state(view->priv->tray) != TRAY_STATE_NOTIFY) { if (str) { gm_tray_set_tip(view->priv->tray, str->str); } else { gm_tray_set_tip(view->priv->tray, NULL); } } if (str) { g_string_free(str, TRUE); } } else { gm_tray_normal(view->priv->tray); } } void gm_app_view_toggle_visibility(GmAppView *view) { gboolean visible = GTK_WIDGET_VISIBLE(view); if (!visible || !gtk_window_is_active(GTK_WINDOW(view))) { if (!visible) { gm_app_view_restore_size(view); gm_app_view_restore_position(view); } gtk_widget_show(GTK_WIDGET(view)); gtk_window_present(GTK_WINDOW(view)); gtk_widget_grab_focus(GTK_WIDGET(view)); if (view->priv->tray) { gm_tray_normal(view->priv->tray); } } else if (view->priv->tray) { gm_app_view_store_position(view); gtk_widget_hide(GTK_WIDGET(view)); } } GmWorldView * gm_app_view_world_view_from_world(GmAppView *view, GmWorld *world) { GtkNotebook *book = view->priv->notebook; GmWorldView *current_view; gint i; for (i = 0; i < gtk_notebook_get_n_pages(book); i++) { current_view = GM_WORLD_VIEW(gtk_notebook_get_nth_page(book, i)); if (gm_world_view_world(current_view) == world) { return current_view; } } return NULL; } /* Callbacks */ void on_gm_app_view_world_close_from_tab(GmWorldTab *tab, GmWorld *world) { gm_world_unload(world); } void on_gm_app_view_world_name_changed(GmWorld *world, GParamSpec *pspec, GmAppView *view) { GmAppViewWorldMenuItem *item = (GmAppViewWorldMenuItem *) g_hash_table_lookup(view->priv->world_menu_items, world); g_object_set(G_OBJECT(item->action), "label", gm_world_name(world), NULL); if (world == gm_app_view_active_world(view)) { gm_app_view_update_title(view); } } void on_gm_app_view_world_added(GmApp *app, GmWorld *world, GmAppView *view) { GmAppViewWorldMenuItem *item = g_new0(GmAppViewWorldMenuItem, 1); gchar *name = g_strconcat("WorldItem", gm_world_name(world), NULL); gchar *tooltip = g_strconcat(_("Open world "), gm_world_name(world), NULL); gchar *path; GList *worlds, *position; gboolean top; // Find the world previous to this one worlds = gm_app_worlds(app); position = g_list_find(worlds, world); if (!position || !position->prev) { path = g_strdup("/MenuBar/WorldMenu/WorldMenuAdditions"); top = TRUE; } else { path = g_strconcat("/MenuBar/WorldMenu/WorldMenuAdditions/WorldItem", gm_world_name((GmWorld *)(position->prev->data)), NULL); top = FALSE; } /* TODO: add custom icon from logo */ item->merge_id = gtk_ui_manager_new_merge_id(view->priv->manager); item->action = gtk_action_new(name, gm_world_name(world), tooltip, NULL); gtk_action_group_add_action(view->priv->worlds_action_group, item->action); gtk_ui_manager_add_ui(view->priv->manager, item->merge_id, path, name, name, GTK_UI_MANAGER_MENUITEM, top); g_free(path); gtk_ui_manager_ensure_update(view->priv->manager); g_signal_connect(item->action, "activate", G_CALLBACK(on_gm_app_view_world_activate), world); g_signal_connect(world, "load", G_CALLBACK(on_gm_app_view_world_load), view); g_signal_connect(world, "unload", G_CALLBACK(on_gm_app_view_world_unload), view); g_signal_connect(world, "activate_request", G_CALLBACK(on_gm_app_view_world_activate_request), view); g_signal_connect(world, "notify::name", G_CALLBACK(on_gm_app_view_world_name_changed), view); g_signal_connect(world, "state_changing", G_CALLBACK(on_gm_app_view_world_state_changing), view); g_signal_connect(world, "notify::active", G_CALLBACK(on_gm_app_view_world_active_changed), view); g_signal_connect(world, "notify::activity", G_CALLBACK(on_gm_app_view_world_activity_changed), view); g_signal_connect(world, "notify_message", G_CALLBACK(on_gm_app_view_world_notify_message), view); g_hash_table_insert(view->priv->world_menu_items, world, item); g_free(name); g_free(tooltip); } void on_gm_app_view_world_state_changing(GmWorld *world, GmNetState state, GmAppView *view) { if (world == gm_app_view_active_world(view)) { switch (state) { case GM_NET_STATE_CONNECTING: gm_app_view_update_connect_button(view, TRUE); break; case GM_NET_STATE_DISCONNECTED: gm_app_view_update_connect_button(view, FALSE); break; default: break; } } } void on_gm_app_view_world_load(GmWorld *world, GmAppView *view) { GtkWidget *world_view = gm_world_view_new(world); GmWorldTab *tab = gm_world_tab_new(world); g_signal_connect(tab, "close", G_CALLBACK(on_gm_app_view_world_close_from_tab), world); g_signal_connect(gm_world_get_mcp_session(world), "package_created", G_CALLBACK(on_gm_app_view_world_mcp_package_created), view); g_signal_connect(world_view, "switch_page", G_CALLBACK(on_gm_app_view_world_view_notebook_switch_page), view); g_signal_connect(world_view, "destroy", G_CALLBACK(on_gm_app_view_world_view_destroy), view); gtk_notebook_append_page(view->priv->notebook, world_view, GTK_WIDGET(tab)); gtk_widget_show_all(world_view); if (gtk_notebook_get_n_pages(view->priv->notebook) == 1) { gm_app_view_worlds_loaded(view); } } void on_gm_app_view_world_unload(GmWorld *world, GmAppView *view) { GmWorldView *world_view = gm_app_view_world_view_from_world(view, world); g_signal_handlers_disconnect_by_func(gm_world_get_mcp_session(world), on_gm_app_view_world_mcp_package_created, view); if (world_view != NULL) { gtk_notebook_remove_page(view->priv->notebook, gtk_notebook_page_num(view->priv->notebook, GTK_WIDGET(world_view))); if (gtk_notebook_get_n_pages(view->priv->notebook) == 0) { gm_app_view_worlds_unloaded(view); } } } void on_gm_app_view_world_activate_request(GmWorld *world, GmAppView *view) { gm_world_set_active(world, TRUE); } void on_gm_app_view_world_notify_message(GmWorld *world, gchar const *message, GmAppView *view) { if (!gtk_window_is_active(GTK_WINDOW(view))) { if (view->priv->tray) { gm_tray_notify(view->priv->tray, message); } gtk_window_set_urgency_hint(GTK_WINDOW(view), TRUE); } } void on_gm_app_view_accel_switch_page(GtkAccelGroup * accelgroup, GObject * arg1, guint arg2, GdkModifierType arg3, AccelInfo *info) { gtk_notebook_set_current_page(info->view->priv->notebook, info->num); } void on_gm_app_view_accel_switch_edit(GtkAccelGroup * accelgroup, GObject * arg1, guint arg2, GdkModifierType arg3, AccelInfo *info) { GmWorldView *view = gm_app_view_active_world_view(info->view); if (view) { gtk_notebook_set_current_page(GTK_NOTEBOOK(view), info->num); } } void on_gm_app_view_accel_cycle_page(GtkAccelGroup * accelgroup, GObject * arg1, guint arg2, GdkModifierType arg3, AccelInfo *info) { GtkNotebook *note = info->view->priv->notebook; gint p = gtk_notebook_get_current_page(note) + info->num; if (p < 0) { gtk_notebook_set_current_page(note, gtk_notebook_get_n_pages(note) - 1); } else if (p > gtk_notebook_get_n_pages(note) - 1) { gtk_notebook_set_current_page(note, 0); } else { gtk_notebook_set_current_page(note, p); } } void on_gm_app_view_world_activate(GtkAction * action, GmWorld *world) { gm_world_load(world); } GmSearchableSearchFlags gm_app_view_search_flags(GmAppView *view) { GmSearchableSearchFlags flags = 0; gboolean active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON( view->priv->check_button_search_direction)); gboolean sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON( view->priv->check_button_search_sensitive)); if (active) { flags = GM_SEARCHABLE_SEARCH_BACKWARDS; } else { flags = GM_SEARCHABLE_SEARCH_FORWARDS; } if (!sensitive) { flags |= GM_SEARCHABLE_SEARCH_CASE_INSENSITIVE; } return flags; } gboolean gm_app_view_find_first(GmAppView *view) { GmWorldView *world_view = gm_app_view_active_world_view(view); GdkColor red; GtkEntry *entry = view->priv->entry_find; gchar const *text; if (world_view) { gdk_color_parse ("#ff6666", &red); text = gtk_entry_get_text(entry); if (gm_world_view_find_first(world_view, text, gm_app_view_search_flags(view)) || *text == '\0') { gtk_widget_modify_base(GTK_WIDGET(entry), GTK_STATE_NORMAL, NULL); } else { gtk_widget_modify_base(GTK_WIDGET(entry), GTK_STATE_NORMAL, &red); } } return FALSE; } void on_gm_app_view_entry_find_changed(GtkEditable *editable, GmAppView *view) { g_idle_add((GSourceFunc)gm_app_view_find_first, view); } void on_gm_app_view_entry_find_activate(GtkEntry *entry, GmAppView *view) { on_gm_app_view_edit_find_next(NULL, view); } void gm_app_view_show_replace_box(GmAppView *view) { if (!GTK_WIDGET_VISIBLE(view->priv->hbox_replace)) { gtk_widget_show(GTK_WIDGET(view->priv->label_replace)); gtk_widget_show(GTK_WIDGET(view->priv->hbox_replace)); } } void gm_app_view_show_find_box(GmAppView *view) { if (!GTK_WIDGET_VISIBLE(view->priv->vbox_find)) { gtk_widget_show(GTK_WIDGET(view->priv->vbox_find)); } } void on_gm_app_view_edit_find(GtkMenuItem * menuitem, GmAppView *view) { GtkWidget *entry = GTK_WIDGET(view->priv->entry_find); gm_app_view_show_find_box(view); gtk_widget_grab_focus(entry); gtk_widget_modify_base(entry, GTK_STATE_NORMAL, NULL); } void on_gm_app_view_edit_find_next(GtkMenuItem * menuitem, GmAppView *view) { gm_app_view_show_find_box(view); gm_world_view_find_next(gm_app_view_active_world_view(view), gtk_entry_get_text(view->priv->entry_find), gm_app_view_search_flags(view)); } void on_gm_app_view_edit_replace(GtkMenuItem * menuitem, GmAppView *view) { GtkWidget *entry = GTK_WIDGET(view->priv->entry_replace); gm_app_view_show_find_box(view); gm_app_view_show_replace_box(view); gtk_widget_grab_focus(entry); } void on_gm_app_view_button_find_clicked(GtkButton *button, gpointer user_data) { on_gm_app_view_edit_find_next(NULL, NULL); } void on_gm_app_view_button_replace_clicked(GtkButton *button, GmAppView *view) { if (gm_world_view_replace(gm_app_view_active_world_view(view), gtk_entry_get_text(view->priv->entry_replace))) { gm_world_view_find_next(gm_app_view_active_world_view(view), gtk_entry_get_text(view->priv->entry_find), gm_app_view_search_flags(view)); } } void on_gm_app_view_button_replace_all_clicked(GtkButton *button, GmAppView *view) { gm_world_view_replace_all(gm_app_view_active_world_view(view), gtk_entry_get_text(view->priv->entry_find), gtk_entry_get_text(view->priv->entry_replace), gm_app_view_search_flags(view)); } void on_gm_app_view_expander_replace(GObject *object, GParamSpec *param_spec, GmAppView *view) { gboolean exp = gtk_expander_get_expanded(view->priv->expander_replace); if (exp) { gm_app_view_show_replace_box(view); } else { gtk_widget_hide(GTK_WIDGET(view->priv->label_replace)); gtk_widget_hide(GTK_WIDGET(view->priv->hbox_replace)); } } gboolean on_gm_app_view_entry_find_key_press(GtkWidget *widget, GdkEventKey *event, GmAppView *view) { if (event->keyval == GDK_Escape) { on_gm_app_view_button_find_close_clicked(NULL, view); gtk_widget_grab_focus(GTK_WIDGET(gm_app_view_active_world_view(view))); return TRUE; } else { return FALSE; } } void on_gm_app_view_button_find_close_clicked(GtkButton *button, GmAppView *view) { GmWorldView *world_view = gm_app_view_active_world_view(view); gtk_widget_hide(GTK_WIDGET(view->priv->vbox_find)); if (world_view) { gm_world_view_set_focus(world_view); } } void on_gm_app_view_check_button_search_sensitive_toggled(GtkToggleButton *button, GmAppView *view) { gboolean active = gtk_toggle_button_get_active(button); gm_options_set_int(gm_app_options(view->priv->application), "search_sensitive", active ? 1 : 0); } void on_gm_app_view_check_button_search_direction_toggled(GtkToggleButton *button, GmAppView *view) { GmWorldView *active_view; gboolean active; if (view->priv->ignore_toggling) { return; } active_view = gm_app_view_active_world_view(view); if (active_view == NULL) { return; } active = gtk_toggle_button_get_active(button); if (gtk_notebook_get_current_page(GTK_NOTEBOOK(active_view)) == 0) { gm_options_set_int(gm_app_options(view->priv->application), "search_direction_world", active ? 1 : 0); } else { gm_options_set_int(gm_app_options(view->priv->application), "search_direction", active ? 1 : 0); } } /* Tab moving from gedit */ gint gm_app_view_find_tab_num_at_pos(GmAppView *view, gint abs_x, gint abs_y) { GtkNotebook *notebook = view->priv->notebook; GtkPositionType tab_pos; int page_num = 0; GtkWidget *page; tab_pos = gtk_notebook_get_tab_pos(notebook); if (notebook->first_tab == NULL) { return -1; } /* For some reason unfullscreen + quick click can cause a wrong click event to be reported to the tab */ /*if (!is_in_notebook_window(notebook, abs_x, abs_y)) { return -1; }*/ while ((page = gtk_notebook_get_nth_page(notebook, page_num)) != NULL) { GtkWidget *tab; gint max_x, max_y; gint x_root, y_root; tab = gtk_notebook_get_tab_label(notebook, page); g_return_val_if_fail(tab != NULL, -1); if (!GTK_WIDGET_MAPPED(GTK_WIDGET(tab))) { ++page_num; continue; } gdk_window_get_origin(GDK_WINDOW(tab->window), &x_root, &y_root); max_x = x_root + tab->allocation.x + tab->allocation.width; max_y = y_root + tab->allocation.y + tab->allocation.height; if (((tab_pos == GTK_POS_TOP) || (tab_pos == GTK_POS_BOTTOM)) && (abs_x <= max_x)) { return page_num; } else if (((tab_pos == GTK_POS_LEFT) || (tab_pos == GTK_POS_RIGHT)) && (abs_y <= max_y)) { return page_num; } ++page_num; } return -1; } void gm_app_view_drag_stop(GmAppView *view) { view->priv->drag_in_progress = FALSE; if (view->priv->motion_notify_handler_id != 0) { g_signal_handler_disconnect(G_OBJECT(view->priv->notebook), view->priv->motion_notify_handler_id); view->priv->motion_notify_handler_id = 0; } } void gm_app_view_drag_start(GmAppView *view, guint32 time) { view->priv->drag_in_progress = TRUE; /* get a new cursor, if necessary */ /* FIXME multi-head */ if (view->priv->cursor == NULL) view->priv->cursor = gdk_cursor_new(GDK_FLEUR); /* grab the pointer */ gtk_grab_add(GTK_WIDGET(view->priv->notebook)); /* FIXME multi-head */ if (!gdk_pointer_is_grabbed()) { gdk_pointer_grab(GTK_WIDGET(view->priv->notebook)->window, FALSE, GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, NULL, view->priv->cursor, time); } } void gm_app_view_move_current_tab(GmAppView *view, gint dest_position) { gint cur_page_num; cur_page_num = gtk_notebook_get_current_page(view->priv->notebook); if (dest_position != cur_page_num) { GtkWidget *cur_tab; cur_tab = gtk_notebook_get_nth_page(view->priv->notebook, cur_page_num); gtk_notebook_reorder_child(view->priv->notebook, cur_tab, dest_position); } } gboolean on_gm_app_view_motion_notify(GtkWidget *widget, GdkEventMotion *event, GmAppView *view) { gint page_num; if (view->priv->drag_in_progress == FALSE) { if (gtk_drag_check_threshold(widget, view->priv->x_start, view->priv->y_start, event->x_root, event->y_root)) { gm_app_view_drag_start(view, event->time); return TRUE; } return FALSE; } page_num = gm_app_view_find_tab_num_at_pos(view, event->x_root, event->y_root); if (page_num != -1) { gm_app_view_move_current_tab(view, page_num); } return FALSE; } gboolean on_gm_app_view_notebook_button_press(GtkNotebook *notebook, GdkEventButton *event, GmAppView *view) { gint tab_clicked; if (view->priv->drag_in_progress) return TRUE; tab_clicked = gm_app_view_find_tab_num_at_pos(view, event->x_root, event->y_root); if ((event->button == 1) && (event->type == GDK_BUTTON_PRESS) && (tab_clicked >= 0)) { view->priv->x_start = event->x_root; view->priv->y_start = event->y_root; view->priv->tab = tab_clicked; view->priv->motion_notify_handler_id = g_signal_connect(G_OBJECT(notebook), "motion-notify-event", G_CALLBACK(on_gm_app_view_motion_notify), view); } else if ((event->type == GDK_BUTTON_PRESS) && (event->button == 3)) { if (tab_clicked == -1) { /* Don't show context menu for non tabs */ return TRUE; } else { /* Switch to the page the mouse is over, but don't consume the event */ gtk_notebook_set_current_page(GTK_NOTEBOOK (notebook), tab_clicked); } } return FALSE; } gboolean on_gm_app_view_notebook_button_release(GtkNotebook *notebook, GdkEventButton *event, GmAppView *view) { if (view->priv->drag_in_progress) { gint cur_page_num; GtkWidget *cur_page; cur_page_num = gtk_notebook_get_current_page(notebook); cur_page = gtk_notebook_get_nth_page(notebook, cur_page_num); /* ungrab the pointer if it's grabbed */ if (gdk_pointer_is_grabbed()) { gdk_pointer_ungrab(event->time); } gtk_grab_remove(GTK_WIDGET(notebook)); } /* This must be called even if a drag isn't happening */ gm_app_view_drag_stop(view); return FALSE; } void on_gm_app_view_world_view_notebook_switch_page(GtkNotebook *notebook, GtkNotebookPage * p, guint page_num, GmAppView *view) { GmWorldView *world_view = GM_WORLD_VIEW(notebook); GtkWidget *page; gboolean direction; gboolean can_replace; gboolean can_find; if (!GTK_IS_WIDGET(view) || !GTK_IS_WIDGET(view->priv->hbox_control_find)) return; can_find = gm_world_view_page_can_find(world_view, page_num); gtk_action_set_sensitive(gm_app_view_action(view, "/MenuBar/EditMenu/EditFindMenu"), can_find); gtk_action_set_sensitive(gm_app_view_action(view, "/MenuBar/EditMenu/EditFindNextMenu"), can_find); gtk_widget_set_sensitive(GTK_WIDGET(view->priv->hbox_control_find), can_find); can_replace = gm_world_view_page_can_replace(world_view, page_num); gtk_action_set_sensitive(gm_app_view_action(view, "/MenuBar/EditMenu/EditReplaceMenu"), can_replace); if (!can_replace) { gtk_widget_hide(GTK_WIDGET(view->priv->hbox_replace)); gtk_widget_hide(GTK_WIDGET(view->priv->label_replace)); gtk_expander_set_expanded(view->priv->expander_replace, FALSE); gtk_widget_set_sensitive(GTK_WIDGET(view->priv->expander_replace), FALSE); } else { gtk_widget_set_sensitive(GTK_WIDGET(view->priv->expander_replace), TRUE); } if (page_num == 0) { direction = gm_options_get_int(gm_app_options(view->priv->application), "search_direction_world"); } else { direction = gm_options_get_int(gm_app_options(view->priv->application), "search_direction"); } view->priv->ignore_toggling = TRUE; gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON( view->priv->check_button_search_direction), direction); view->priv->ignore_toggling = FALSE; /* Enable/disable editor toolbar */ page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(world_view), page_num); gtk_action_group_set_visible(view->priv->editor_action_group, GM_IS_EDITOR_VIEW(page)); gtk_action_group_set_sensitive(view->priv->editor_action_group, GM_IS_EDITOR_VIEW(page)); #ifdef HAVE_PARSER if (GM_IS_EDITOR_VIEW(page)) { gtk_action_set_sensitive(gm_app_view_action(view, "/ToolBar/EditorToolAdditions/EditorParseTool"), gm_editor_is_code( gm_editor_view_editor(GM_EDITOR_VIEW(page)))); } #endif } void on_gm_app_view_notebook_switch_page(GtkNotebook * notebook, GtkNotebookPage * page, guint page_num, GmAppView *view) { GmWorld *world = gm_app_view_active_world(view); if (view->priv->active_world == world) { return; } if (view->priv->active_world) { gm_world_set_active(view->priv->active_world, FALSE); } if (world) { view->priv->active_world = world; gm_world_set_active(view->priv->active_world, TRUE); on_gm_app_view_world_view_notebook_switch_page(GTK_NOTEBOOK( gm_app_view_active_world_view(view)), NULL, 0, view); } g_signal_emit(view, gm_app_view_signals[ACTIVE_WORLD_CHANGED], 0, gm_app_view_active_world_view(view)); } GmEditorView * gm_app_view_active_editor_view(GmAppView *view) { GmWorldView *world_view = gm_app_view_active_world_view(view); gint page = gtk_notebook_get_current_page(GTK_NOTEBOOK(world_view)); return GM_EDITOR_VIEW(gtk_notebook_get_nth_page(GTK_NOTEBOOK(world_view), page)); } void on_gm_app_view_world_active_changed(GmWorld *world, GParamSpec *pspec, GmAppView *view) { GmWorldView *world_view = gm_app_view_world_view_from_world(view, world); if (gm_world_active(world)) { gtk_notebook_set_current_page(view->priv->notebook, gtk_notebook_page_num(view->priv->notebook, GTK_WIDGET(world_view))); gm_app_view_update_title(view); switch (gm_world_state(world)) { case GM_NET_STATE_CONNECTING: case GM_NET_STATE_CONNECTED: gm_app_view_update_connect_button(view, TRUE); break; case GM_NET_STATE_DISCONNECTED: gm_app_view_update_connect_button(view, FALSE); break; default: break; } } } void on_gm_app_view_world_activity_changed(GmWorld *world, GParamSpec *pspec, GmAppView *view) { gm_app_view_update_tray(view); } void on_gm_app_view_world_mcp_package_created(GmMcpSession *session, GmMcpPackage *package, GmAppView *view) { gm_mcp_package_create_view(package, G_OBJECT(view)); } void on_gm_app_view_world_view_destroy(GmWorldView *world_view, GmAppView *view) { GmWorld *world = gm_world_view_world(world_view); if (gm_world_loaded(world)) { // This only happens when the main view is destroyed when there are // still worlds active. The thing is we need to unload the world here // so that anything GUI related (like mcp things) can proper finalize // Disconnect the unload handler to avoid strange things to happen g_signal_handlers_disconnect_by_func(world, on_gm_app_view_world_unload, view); gm_world_unload(world); } } gboolean on_gm_app_view_tray_button_press(GmTray *tray, GdkEventButton *event, GmAppView *view) { if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) { return FALSE; } if (event->button == 1) { gm_app_view_toggle_visibility(view); } return FALSE; } void on_gm_app_view_tray_message_clicked(GmTray *tray, GmAppView *view) { gboolean visible = GTK_WIDGET_VISIBLE(view); if (!visible || !gtk_window_is_active(GTK_WINDOW(view))) { gm_app_view_toggle_visibility(view); gdk_window_focus(GTK_WIDGET(view)->window, GDK_CURRENT_TIME); } } gboolean idle_create_tray(gpointer user_data) { GmAppView *view = GM_APP_VIEW(user_data); view->priv->tray_idle_create = 0; gm_app_view_create_tray(view); if (!gm_tray_has_manager() && !GTK_WIDGET_VISIBLE(view)) { gm_app_view_toggle_visibility(view); gtk_window_present(GTK_WINDOW(view)); } return FALSE; } void on_gm_app_view_tray_destroy(GtkWidget *caller, GmAppView *view) { // Make sure the view is visible g_object_unref(G_OBJECT(caller)); view->priv->tray = NULL; view->priv->tray_idle_create = g_idle_add(idle_create_tray, view); }