This repository has been archived on 2020-04-11. You can view files and clone it, but cannot push or open issues or pull requests.
gnoemoe/gnoemoe/widgets/gm-app-view.c

1735 lines
50 KiB
C

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <glade/glade.h>
#include <libgnomeui/libgnomeui.h>
#include <libgnome/gnome-url.h>
#include <vte/vte.h>
#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);
}