355c7e8a41
* po/POTFILES.in: added gnoemoe/dialogs/gm-world-paste-dialog.c and ui/gm-world-paste.glade * po/nl.po: added translations * ui/Makefile.am: * ui/gm-ui.xml: * ui/gm-world-paste.glade: new paste dialog * gnoemoe/mcp/Makefile.include: added gm-cell-renderer-text.[ch] * gnoemoe/mcp/gm-cell-renderer-text.[ch]: new cell renderer for rendering userlist * gnoemoe/mcp/gm-mcp-vmoo-client.c: update metrics in timeout so to reduce the number of updates when resizing * gnoemoe/mcp/gm-mcp-icecrew-serverinfo.c: max version set to 1.0 (1.0 does not actively request the info because it will be send on initialization). Set menu item invisible instead of insensitive when there is no info available * gnoemoe/mcp/gm-mcp-icecrew-userlist.c: fixed menu item substitution * gnoemoe/mcp/gm-mcp-vmoo-userlist.c: removed support for status because it doesn't really support it * gm-mcp-userlist-view.[ch]: moved column constants to header. Render items with new gm-cell-renderer-text. * gnoemoe/mcp/gm-mcp-icecrew-playerdb.[ch]: made gm_mcp_icecrew_playerdb_players public * gnoemoe/dialogs/Makefile.include: added gm-world-paste-dialog * gnoemoe/dialogs/gm-world-paste-dialog.[ch]: new paste dialog * gnoemoe/dialogs/gm-world-properties-dialog.c: * gnoemoe/dialogs/gm-world-logs-dialog.c: fixed leaking tree stores * gnoemoe/widgets/Makefile.include: added gm-commands.[ch] * gnoemoe/widgets/gm-commands.[ch]: new file for handling action activation (removed from gm-app-view) * gnoemoe/widgets/gm-world-view.c: fixed userlist size restore * gnoemoe/widgets/gm-app-view.c: removed action handlers * gnoemoe/gm-support.[ch]: added gm_find_child * gnoemoe/gm-world.c: removed debug message * gnoemoe/gm-ui.h: changed actions to gm-commands
1021 lines
28 KiB
C
1021 lines
28 KiB
C
#include <gdk/gdkkeysyms.h>
|
|
#include <string.h>
|
|
|
|
#include "gm-app.h"
|
|
#include "gm-world-view.h"
|
|
#include "gm-world-text-view.h"
|
|
#include "gm-world-input-view.h"
|
|
#include "gm-text-scroller.h"
|
|
#include "gm-editor-view.h"
|
|
#include "gm-embedded-view.h"
|
|
#include "gm-external-view.h"
|
|
#include "gm-log-view.h"
|
|
|
|
#include "gm-debug.h"
|
|
#include "gm-support.h"
|
|
#include "gm-color-table.h"
|
|
#include "mcp/gm-mcp-package.h"
|
|
#include "mcp/gm-mcp-session.h"
|
|
#include "gm-editor.h"
|
|
#include "gm-searchable.h"
|
|
|
|
#define GM_WORLD_VIEW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), \
|
|
GM_TYPE_WORLD_VIEW, GmWorldViewPrivate))
|
|
|
|
struct _GmWorldViewPrivate {
|
|
GmWorld *world;
|
|
|
|
GtkStatusbar *statusbar;
|
|
gchar *status_msg;
|
|
guint status_timeout;
|
|
GtkHPaned *hpaned;
|
|
GmWorldTextView *text_view_world;
|
|
GmWorldInputView *text_view_input;
|
|
GmTextScroller *text_scroller_world;
|
|
|
|
GList *external_editors;
|
|
};
|
|
|
|
void on_gm_world_view_world_text_received(GmWorld *world, gchar *text,
|
|
GmWorldView *view);
|
|
void on_gm_world_view_world_error(GmWorld *world, gchar *text, gint code,
|
|
GmWorldView *view);
|
|
void on_gm_world_input_view_world_text_activate(GmWorldInputView *iview,
|
|
gchar *text, GmWorldView *view);
|
|
void on_gm_world_view_world_state_changing(GmWorld *world, guint state,
|
|
GmWorldView *view);
|
|
|
|
void on_gm_world_view_world_active_changed(GmWorld *world, GParamSpec *pspec,
|
|
GmWorldView *view);
|
|
|
|
void on_gm_world_view_world_highlight(GmWorld *world, gint start, gint end,
|
|
gchar *color, GmWorldView *view);
|
|
|
|
void on_gm_world_view_world_editor_added(GmWorld *world, GmEditor *editor,
|
|
GmWorldView *view);
|
|
void on_gm_world_view_world_editor_removed(GmWorld *world, GmEditor *editor,
|
|
GmWorldView *view);
|
|
|
|
void on_gm_world_view_world_mcp_package_created(GmMcpSession *session,
|
|
GmMcpPackage *package, GmWorldView *view);
|
|
|
|
void on_gm_world_view_editor_view_modified_changed(GmEditorView *editor_view,
|
|
gboolean modified, GmWorldView *view);
|
|
gboolean on_gm_world_view_world_text_view_scroll_event(GmWorldView *view,
|
|
GdkEventScroll *event, GmWorldTextView *text);
|
|
void on_gm_world_view_world_text_view_url_activate(GmWorldView *view,
|
|
gchar const *url);
|
|
void on_gm_world_view_editor_view_close_clicked(GtkButton *button,
|
|
GtkWidget *view);
|
|
void on_gm_world_view_log_view_close_clicked(GtkButton *button,
|
|
GtkWidget *view);
|
|
|
|
gboolean on_gm_world_view_world_input_view_key_pressed(GtkWidget *widget,
|
|
GdkEventKey *event, GmWorldView *view);
|
|
|
|
/* Signals
|
|
|
|
enum {
|
|
NUM_SIGNALS
|
|
};
|
|
|
|
static guint gm_world_view_signals[NUM_SIGNALS] = {0};*/
|
|
|
|
static void gm_world_view_searchable_iface_init(
|
|
GmSearchableInterface *iface);
|
|
|
|
static GtkTextView *gm_world_view_searchable_get_text_view(GmSearchable *sea);
|
|
|
|
G_DEFINE_TYPE_EXTENDED(GmWorldView, gm_world_view, GTK_TYPE_NOTEBOOK, 0, \
|
|
G_IMPLEMENT_INTERFACE(GM_TYPE_SEARCHABLE, \
|
|
gm_world_view_searchable_iface_init))
|
|
|
|
static void
|
|
gm_world_view_searchable_iface_init(GmSearchableInterface *iface) {
|
|
iface->get_text_view = gm_world_view_searchable_get_text_view;
|
|
}
|
|
|
|
static GtkTextView *
|
|
gm_world_view_searchable_get_text_view(GmSearchable *sea) {
|
|
GmWorldView *view = (GmWorldView *)(sea);
|
|
|
|
g_return_val_if_fail(GM_IS_WORLD_VIEW(sea), NULL);
|
|
|
|
return GTK_TEXT_VIEW(view->priv->text_view_world);
|
|
}
|
|
|
|
static void
|
|
gm_world_view_finalize(GObject *object) {
|
|
GmWorldView *view = GM_WORLD_VIEW(object);
|
|
GList *ext;
|
|
|
|
for (ext = view->priv->external_editors; ext; ext = ext->next) {
|
|
gm_external_view_destroy(GM_EXTERNAL_VIEW(ext->data));
|
|
}
|
|
|
|
if (view->priv->status_timeout) {
|
|
g_source_remove(view->priv->status_timeout);
|
|
}
|
|
|
|
g_free(view->priv->status_msg);
|
|
|
|
g_list_free(view->priv->external_editors);
|
|
|
|
g_signal_handlers_disconnect_by_func(view->priv->world,
|
|
G_CALLBACK(on_gm_world_view_world_text_received), view);
|
|
g_signal_handlers_disconnect_by_func(view->priv->world,
|
|
G_CALLBACK(on_gm_world_view_world_error), view);
|
|
g_signal_handlers_disconnect_by_func(view->priv->world,
|
|
G_CALLBACK(on_gm_world_view_world_state_changing), view);
|
|
g_signal_handlers_disconnect_by_func(view->priv->world,
|
|
G_CALLBACK(on_gm_world_view_world_active_changed), view);
|
|
g_signal_handlers_disconnect_by_func(view->priv->world,
|
|
G_CALLBACK(on_gm_world_view_world_highlight), view);
|
|
g_signal_handlers_disconnect_by_func(view->priv->world,
|
|
G_CALLBACK(on_gm_world_view_world_editor_added), view);
|
|
g_signal_handlers_disconnect_by_func(view->priv->world,
|
|
G_CALLBACK(on_gm_world_view_world_editor_removed), view);
|
|
|
|
g_signal_handlers_disconnect_by_func(
|
|
gm_world_get_mcp_session(view->priv->world),
|
|
G_CALLBACK(on_gm_world_view_world_mcp_package_created), view);
|
|
|
|
g_object_unref(view->priv->world);
|
|
G_OBJECT_CLASS(gm_world_view_parent_class)->finalize(object);
|
|
}
|
|
|
|
|
|
static void
|
|
gm_world_view_destroy(GtkObject *object) {
|
|
GmWorldView *view = GM_WORLD_VIEW(object);
|
|
|
|
if (!GM_IS_WORLD_VIEW(view) || !GTK_IS_WIDGET(view->priv->hpaned)) {
|
|
return;
|
|
}
|
|
|
|
gm_options_set_int(gm_world_options(view->priv->world), "pane_position",
|
|
GTK_WIDGET(view->priv->hpaned)->allocation.width -
|
|
gtk_paned_get_position(GTK_PANED(view->priv->hpaned)));
|
|
|
|
gm_options_save(gm_world_options(view->priv->world));
|
|
|
|
if (GTK_OBJECT_CLASS(gm_world_view_parent_class)->destroy) {
|
|
GTK_OBJECT_CLASS(gm_world_view_parent_class)->destroy(object);
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean
|
|
gm_world_view_input_grab_focus(GmWorldView *view) {
|
|
gtk_widget_grab_focus(GTK_WIDGET(view->priv->text_view_input));
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gm_world_view_switch_page(GtkNotebook *notebook, GtkNotebookPage *page,
|
|
guint page_num) {
|
|
if (GTK_NOTEBOOK_CLASS(gm_world_view_parent_class)->switch_page) {
|
|
GTK_NOTEBOOK_CLASS(gm_world_view_parent_class)->switch_page(notebook, page, page_num);
|
|
}
|
|
|
|
if (page_num == 0) {
|
|
g_idle_add((GSourceFunc)gm_world_view_input_grab_focus,
|
|
GM_WORLD_VIEW(notebook));
|
|
}
|
|
}
|
|
|
|
static void
|
|
gm_world_view_remove_page(GtkContainer *container, GtkWidget *page) {
|
|
GtkNotebook *notebook = GTK_NOTEBOOK(container);
|
|
|
|
if (GTK_CONTAINER_CLASS(gm_world_view_parent_class)->remove) {
|
|
GTK_CONTAINER_CLASS(gm_world_view_parent_class)->remove(container, page);
|
|
}
|
|
|
|
if (gtk_notebook_get_n_pages(notebook) == 1) {
|
|
gtk_notebook_set_show_tabs(notebook, FALSE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gm_world_view_class_init(GmWorldViewClass *klass) {
|
|
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
|
GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS(klass);
|
|
GtkContainerClass *container_class = GTK_CONTAINER_CLASS(klass);
|
|
|
|
object_class->finalize = gm_world_view_finalize;
|
|
gtk_object_class->destroy = gm_world_view_destroy;
|
|
GTK_NOTEBOOK_CLASS(klass)->switch_page = gm_world_view_switch_page;
|
|
container_class->remove = gm_world_view_remove_page;
|
|
|
|
g_type_class_add_private(object_class, sizeof(GmWorldViewPrivate));
|
|
}
|
|
|
|
static void
|
|
gm_world_view_ensure_show_tabs(GmWorldView *view) {
|
|
if (!gtk_notebook_get_show_tabs(GTK_NOTEBOOK(view))) {
|
|
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(view), TRUE);
|
|
}
|
|
}
|
|
|
|
/*gboolean
|
|
timeout_text(gpointer user_data) {
|
|
GtkTextView *view = GTK_TEXT_VIEW(user_data);
|
|
GtkTextBuffer *buffer = gtk_text_view_get_buffer(view);
|
|
GtkTextIter end, start;
|
|
static GdkEvent *event_press = NULL;
|
|
static GdkEvent *event_release = NULL;
|
|
gboolean result;
|
|
|
|
gtk_text_buffer_get_bounds(buffer, &start, &end);
|
|
|
|
if (gtk_text_iter_get_offset(&end) > 80) {
|
|
gtk_text_buffer_delete(buffer, &start, &end);
|
|
} else {
|
|
if (!event_press) {
|
|
gtk_widget_realize(GTK_WIDGET(view));
|
|
event_press = gdk_event_new(GDK_KEY_PRESS);
|
|
event_press->key.keyval = 119;
|
|
event_press->key.window = gtk_text_view_get_window(view, GTK_TEXT_WINDOW_TEXT);
|
|
|
|
event_release = gdk_event_new(GDK_KEY_RELEASE);
|
|
event_release->key.keyval = 119;
|
|
event_release->key.window = event_press->key.window;
|
|
}
|
|
|
|
event_press->key.time = gtk_get_current_event_time();
|
|
event_release->key.time = gtk_get_current_event_time();
|
|
|
|
g_signal_emit_by_name(GTK_WIDGET(view), "key_press_event", event_press, &result);
|
|
g_signal_emit_by_name(GTK_WIDGET(view), "key_release_event", event_release, &result);
|
|
}
|
|
|
|
return TRUE;
|
|
}*/
|
|
|
|
GtkWidget *
|
|
gm_world_view_create_input_text_view(GmWorldView *view) {
|
|
GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
|
|
GtkWidget *input_text_view = gm_world_input_view_new_with_color_table(
|
|
gm_app_color_table(gm_app_instance()));
|
|
|
|
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
|
|
GTK_POLICY_NEVER, GTK_POLICY_NEVER);
|
|
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
|
|
GTK_SHADOW_IN);
|
|
gtk_container_add(GTK_CONTAINER(scrolled_window), input_text_view);
|
|
|
|
view->priv->text_view_input = GM_WORLD_INPUT_VIEW(input_text_view);
|
|
|
|
g_signal_connect(input_text_view, "key_press_event",
|
|
G_CALLBACK(on_gm_world_view_world_input_view_key_pressed), view);
|
|
return scrolled_window;
|
|
}
|
|
|
|
GtkWidget *
|
|
gm_world_view_create_world_text_view(GmWorldView *view) {
|
|
GtkWidget *world_text_view = gm_world_text_view_new_with_color_table(
|
|
gm_app_color_table(gm_app_instance()));
|
|
GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
|
|
|
|
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
|
|
GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
|
|
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
|
|
GTK_SHADOW_IN);
|
|
gtk_container_add(GTK_CONTAINER(scrolled_window), world_text_view);
|
|
|
|
view->priv->text_view_world = GM_WORLD_TEXT_VIEW(world_text_view);
|
|
|
|
// Create new text scroller, this object will take care of itself and will
|
|
// destroy itself when the view dies, neat!
|
|
view->priv->text_scroller_world =
|
|
gm_text_scroller_new(GTK_TEXT_VIEW(view->priv->text_view_world));
|
|
|
|
g_signal_connect(world_text_view, "scroll_event",
|
|
G_CALLBACK(on_gm_world_view_world_text_view_scroll_event), view);
|
|
g_signal_connect(world_text_view, "url_activate",
|
|
G_CALLBACK(on_gm_world_view_world_text_view_url_activate), view);
|
|
|
|
return scrolled_window;
|
|
}
|
|
|
|
void
|
|
gm_world_view_update_status(GmWorldView *view, gchar const *status) {
|
|
gtk_statusbar_pop(view->priv->statusbar, 0);
|
|
|
|
if (status == NULL) {
|
|
gtk_statusbar_push(view->priv->statusbar, 0,
|
|
_("Welcome to GnoeMoe, explorer of new worlds!"));
|
|
} else {
|
|
gtk_statusbar_push(view->priv->statusbar, 0,
|
|
status);
|
|
}
|
|
}
|
|
|
|
void
|
|
gm_world_view_flash_status(GmWorldView *view, gchar const *status,
|
|
guint seconds) {
|
|
|
|
}
|
|
|
|
GtkWidget *
|
|
gm_world_view_log_page_new(GmWorldView *view, gchar const *filename) {
|
|
gchar *text = gm_read_file(filename);
|
|
gchar *base;
|
|
GmLabelInfo info;
|
|
GtkWidget *log_view;
|
|
GtkWidget *label;
|
|
|
|
if (text == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
base = g_path_get_basename(filename);
|
|
label = gm_create_tab_label("editor_text.xpm", base, TRUE, &info);
|
|
g_free(base);
|
|
|
|
log_view = GTK_WIDGET(gm_log_view_new());
|
|
gtk_widget_show(log_view);
|
|
|
|
gtk_notebook_append_page(GTK_NOTEBOOK(view), log_view, label);
|
|
|
|
gm_world_view_ensure_show_tabs(view);
|
|
|
|
gtk_notebook_set_current_page(GTK_NOTEBOOK(view),
|
|
gtk_notebook_page_num(GTK_NOTEBOOK(view), log_view));
|
|
|
|
g_signal_connect(info.button_exit, "clicked",
|
|
G_CALLBACK(on_gm_world_view_log_view_close_clicked),
|
|
log_view);
|
|
|
|
gm_log_view_set_text(GM_LOG_VIEW(log_view), text);
|
|
g_free(text);
|
|
|
|
return log_view;
|
|
}
|
|
|
|
GtkWidget *
|
|
gm_world_view_editor_create_view(GmWorldView *view, GmEditor *editor) {
|
|
GtkWidget *editor_view;
|
|
GmOptions *options = gm_app_options(gm_app_instance());
|
|
if (strcmp(gm_options_get(options, "editor_alternative"),
|
|
"0") != 0) {
|
|
if (gm_options_get_int(options, "editor_embed")) {
|
|
editor_view = GTK_WIDGET(gm_embedded_view_new(view->priv->world,
|
|
editor));
|
|
} else {
|
|
view->priv->external_editors = g_list_append(
|
|
view->priv->external_editors,
|
|
gm_external_view_new(view->priv->world, editor));
|
|
return NULL;
|
|
}
|
|
} else {
|
|
editor_view = GTK_WIDGET(gm_editor_view_new(view->priv->world, editor));
|
|
g_signal_connect(editor_view, "modified-changed",
|
|
G_CALLBACK(on_gm_world_view_editor_view_modified_changed),
|
|
view);
|
|
}
|
|
|
|
gtk_widget_show(editor_view);
|
|
return editor_view;
|
|
}
|
|
|
|
GtkWidget *
|
|
gm_world_view_editor_page_new(GmWorldView *view, GmEditor *editor) {
|
|
GtkWidget *label;
|
|
GtkWidget *editor_view;
|
|
|
|
GmLabelInfo info;
|
|
gchar const *icon;
|
|
|
|
if (gm_editor_is_code(editor)) {
|
|
icon = "editor_verb.xpm";
|
|
} else {
|
|
icon = "editor_text.xpm";
|
|
}
|
|
|
|
label = gm_create_tab_label("editor_verb.xpm", gm_editor_name(editor),
|
|
TRUE, &info);
|
|
|
|
editor_view = gm_world_view_editor_create_view(view, editor);
|
|
|
|
if (editor_view == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
gm_world_view_ensure_show_tabs(view);
|
|
|
|
gtk_notebook_append_page(GTK_NOTEBOOK(view), editor_view, label);
|
|
gtk_notebook_set_current_page(GTK_NOTEBOOK(view),
|
|
gtk_notebook_page_num(GTK_NOTEBOOK(view), editor_view));
|
|
|
|
g_signal_connect(info.button_exit, "clicked",
|
|
G_CALLBACK(on_gm_world_view_editor_view_close_clicked),
|
|
editor_view);
|
|
|
|
return editor_view;
|
|
}
|
|
|
|
GtkWidget *
|
|
gm_world_view_world_page_new(GmWorldView *view) {
|
|
GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
|
|
GtkWidget *hpaned = gtk_hpaned_new();
|
|
GtkWidget *status = gtk_statusbar_new();
|
|
GtkWidget *vbox_world = gtk_vbox_new(FALSE, 3);
|
|
|
|
gtk_box_pack_start(GTK_BOX(vbox_world),
|
|
gm_world_view_create_world_text_view(view), TRUE, TRUE, 0);
|
|
gtk_box_pack_start(GTK_BOX(vbox_world),
|
|
gm_world_view_create_input_text_view(view), FALSE, FALSE, 0);
|
|
|
|
gtk_container_set_border_width(GTK_CONTAINER(vbox_world), 3);
|
|
gtk_paned_pack1(GTK_PANED(hpaned), vbox_world, TRUE, TRUE);
|
|
|
|
gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(status), FALSE);
|
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0);
|
|
gtk_box_pack_start(GTK_BOX(vbox), status, FALSE, FALSE, 0);
|
|
|
|
view->priv->statusbar = GTK_STATUSBAR(status);
|
|
view->priv->hpaned = GTK_HPANED(hpaned);
|
|
|
|
gm_world_view_update_status(view, NULL);
|
|
|
|
return vbox;
|
|
}
|
|
|
|
static gboolean
|
|
paned_restore_size(GmWorldView *view) {
|
|
GtkWidget *widget = GTK_WIDGET(view->priv->hpaned);
|
|
|
|
gtk_paned_set_position(GTK_PANED(widget),
|
|
widget->allocation.width
|
|
- gm_options_get_int(gm_world_options(view->priv->world),
|
|
"pane_position"));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gm_world_view_restore_paned_size(GtkWidget *widget, GmWorldView *view) {
|
|
g_idle_add((GSourceFunc)paned_restore_size, view);
|
|
|
|
/* CHECK: chain up */
|
|
|
|
/* only run this once */
|
|
g_signal_handlers_disconnect_by_func(widget,
|
|
gm_world_view_restore_paned_size, view);
|
|
}
|
|
|
|
static void
|
|
gm_world_view_init(GmWorldView *view) {
|
|
GtkWidget *label;
|
|
GmLabelInfo info;
|
|
|
|
view->priv = GM_WORLD_VIEW_GET_PRIVATE(view);
|
|
|
|
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(view), FALSE);
|
|
gtk_notebook_set_show_border(GTK_NOTEBOOK(view), FALSE);
|
|
gtk_notebook_set_tab_pos(GTK_NOTEBOOK(view), GTK_POS_BOTTOM);
|
|
gtk_notebook_set_scrollable(GTK_NOTEBOOK(view), TRUE);
|
|
|
|
label = gm_create_tab_label("world.svg", _("World"), FALSE, &info);
|
|
gtk_notebook_append_page(GTK_NOTEBOOK(view),
|
|
gm_world_view_world_page_new(view), label);
|
|
|
|
g_signal_connect(view->priv->hpaned, "map",
|
|
G_CALLBACK(gm_world_view_restore_paned_size), view);
|
|
}
|
|
|
|
GtkWidget *
|
|
gm_world_view_new(GmWorld *world) {
|
|
GmWorldView *view = GM_WORLD_VIEW(g_object_new(GM_TYPE_WORLD_VIEW, NULL));
|
|
|
|
view->priv->world = g_object_ref(world);
|
|
|
|
gm_world_input_view_set_history(view->priv->text_view_input,
|
|
gm_world_history(view->priv->world));
|
|
|
|
g_signal_connect(world, "text_received",
|
|
G_CALLBACK(on_gm_world_view_world_text_received), view);
|
|
g_signal_connect(world, "world_error",
|
|
G_CALLBACK(on_gm_world_view_world_error), view);
|
|
g_signal_connect(world, "state_changing",
|
|
G_CALLBACK(on_gm_world_view_world_state_changing), view);
|
|
g_signal_connect(world, "notify::active",
|
|
G_CALLBACK(on_gm_world_view_world_active_changed), view);
|
|
g_signal_connect(world, "highlight",
|
|
G_CALLBACK(on_gm_world_view_world_highlight), view);
|
|
|
|
g_signal_connect(world, "editor_added",
|
|
G_CALLBACK(on_gm_world_view_world_editor_added), view);
|
|
g_signal_connect(world, "editor_removed",
|
|
G_CALLBACK(on_gm_world_view_world_editor_removed), view);
|
|
|
|
g_signal_connect(gm_world_get_mcp_session(world), "package_created",
|
|
G_CALLBACK(on_gm_world_view_world_mcp_package_created), view);
|
|
|
|
g_signal_connect(view->priv->text_view_input, "text_activate",
|
|
G_CALLBACK(on_gm_world_input_view_world_text_activate), view);
|
|
|
|
return GTK_WIDGET(view);
|
|
}
|
|
|
|
gboolean
|
|
gm_world_view_text_active(GmWorldView *view) {
|
|
return gtk_notebook_get_current_page(GTK_NOTEBOOK(view)) == 0;
|
|
}
|
|
|
|
gboolean
|
|
gm_world_view_page_can_find(GmWorldView *view, gint page_num) {
|
|
GtkWidget *page;
|
|
GmSearchable *sea;
|
|
|
|
if (page_num == -1) {
|
|
return FALSE;
|
|
} else if (page_num == 0) {
|
|
return gm_searchable_can_find(GM_SEARCHABLE(view));
|
|
} else {
|
|
page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(view), page_num);
|
|
|
|
if (GM_IS_SEARCHABLE(page)) {
|
|
sea = GM_SEARCHABLE(page);
|
|
|
|
return gm_searchable_can_find(sea);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gm_world_view_can_find(GmWorldView *view) {
|
|
gint np = gtk_notebook_get_current_page(GTK_NOTEBOOK(view));
|
|
return gm_world_view_page_can_find(view, np);
|
|
}
|
|
|
|
gboolean
|
|
gm_world_view_find_first(GmWorldView *view, const gchar *str,
|
|
GmSearchableSearchFlags flags) {
|
|
gint np = gtk_notebook_get_current_page(GTK_NOTEBOOK(view));
|
|
GtkWidget *page;
|
|
GmSearchable *sea;
|
|
|
|
if (np == 0) {
|
|
return gm_searchable_find_first(GM_SEARCHABLE(view), str, flags);
|
|
} else {
|
|
page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(view), np);
|
|
|
|
if (GM_IS_SEARCHABLE(page)) {
|
|
sea = GM_SEARCHABLE(page);
|
|
|
|
if (gm_searchable_can_find(sea)) {
|
|
return gm_searchable_find_first(sea, str, flags);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gm_world_view_find_next(GmWorldView *view, const gchar *str,
|
|
GmSearchableSearchFlags flags) {
|
|
gint np = gtk_notebook_get_current_page(GTK_NOTEBOOK(view));
|
|
GtkWidget *page;
|
|
GmSearchable *sea;
|
|
|
|
if (np == 0) {
|
|
return gm_searchable_find_next(GM_SEARCHABLE(view), str, flags);
|
|
} else {
|
|
page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(view), np);
|
|
|
|
if (GM_IS_SEARCHABLE(page)) {
|
|
sea = GM_SEARCHABLE(page);
|
|
|
|
if (gm_searchable_can_find(sea)) {
|
|
return gm_searchable_find_next(sea, str, flags);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gm_world_view_replace(GmWorldView *view, gchar const *replace) {
|
|
gint np = gtk_notebook_get_current_page(GTK_NOTEBOOK(view));
|
|
GtkWidget *page;
|
|
GmSearchable *sea;
|
|
|
|
if (np == 0) {
|
|
return FALSE;
|
|
} else {
|
|
page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(view), np);
|
|
|
|
if (GM_IS_SEARCHABLE(page)) {
|
|
sea = GM_SEARCHABLE(page);
|
|
|
|
if (gm_searchable_can_replace(sea)) {
|
|
return gm_searchable_replace(sea, replace);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gm_world_view_replace_all(GmWorldView *view, gchar const *str,
|
|
gchar const *replace, GmSearchableSearchFlags flags) {
|
|
gint np = gtk_notebook_get_current_page(GTK_NOTEBOOK(view));
|
|
GtkWidget *page;
|
|
GmSearchable *sea;
|
|
|
|
if (np == 0) {
|
|
return FALSE;
|
|
} else {
|
|
page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(view), np);
|
|
|
|
if (GM_IS_SEARCHABLE(page)) {
|
|
sea = GM_SEARCHABLE(page);
|
|
|
|
if (gm_searchable_can_replace(sea)) {
|
|
return gm_searchable_replace_all(sea, str, replace, flags);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gm_world_view_can_replace(GmWorldView *view) {
|
|
gint np = gtk_notebook_get_current_page(GTK_NOTEBOOK(view));
|
|
return gm_world_view_page_can_replace(view, np);
|
|
}
|
|
|
|
gboolean
|
|
gm_world_view_page_can_replace(GmWorldView *view, gint page_num) {
|
|
GtkWidget *page;
|
|
GmSearchable *sea;
|
|
|
|
if (page_num == -1) {
|
|
return FALSE;
|
|
} else if (page_num == 0) {
|
|
return gm_searchable_can_replace(GM_SEARCHABLE(view));
|
|
} else {
|
|
page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(view), page_num);
|
|
|
|
if (GM_IS_SEARCHABLE(page)) {
|
|
sea = GM_SEARCHABLE(page);
|
|
return gm_searchable_can_replace(sea);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
GmWorld *
|
|
gm_world_view_world(GmWorldView *view) {
|
|
return view->priv->world;
|
|
}
|
|
|
|
GmWorldInputView *
|
|
gm_world_view_input(GmWorldView *view) {
|
|
return view->priv->text_view_input;
|
|
}
|
|
|
|
GmWorldTextView *
|
|
gm_world_view_text_view(GmWorldView *view) {
|
|
return view->priv->text_view_world;
|
|
}
|
|
|
|
GtkTextBuffer *
|
|
gm_world_view_buffer(GmWorldView *view) {
|
|
return gtk_text_view_get_buffer(GTK_TEXT_VIEW(view->priv->text_view_world));
|
|
}
|
|
|
|
GtkHPaned *
|
|
gm_world_view_hpaned(GmWorldView *view) {
|
|
return view->priv->hpaned;
|
|
}
|
|
|
|
void
|
|
gm_world_view_open_log(GmWorldView *view, const gchar *filename) {
|
|
gm_world_view_log_page_new(view, filename);
|
|
}
|
|
|
|
void
|
|
gm_world_view_set_userlist_width(GmWorldView *view, gint width) {
|
|
gtk_paned_set_position(GTK_PANED(view->priv->hpaned),
|
|
GTK_WIDGET(view->priv->hpaned)->allocation.width - width);
|
|
}
|
|
|
|
void
|
|
gm_world_view_set_focus(GmWorldView *view) {
|
|
gtk_widget_grab_focus(GTK_WIDGET(view->priv->text_view_input));
|
|
}
|
|
|
|
void
|
|
gm_world_view_change_font_size(GmWorldView *view, gint size_change) {
|
|
GtkStyle *style = gtk_widget_get_style(GTK_WIDGET(view));
|
|
PangoFontDescription *desc = style->font_desc;
|
|
PangoFontDescription *copy = pango_font_description_copy(desc);
|
|
gchar *new_font;
|
|
|
|
pango_font_description_set_size(copy,
|
|
pango_font_description_get_size(copy) +
|
|
(size_change * PANGO_SCALE));
|
|
new_font = pango_font_description_to_string(copy);
|
|
gm_color_table_set_font_description(gm_app_color_table(gm_app_instance()),
|
|
new_font);
|
|
|
|
pango_font_description_free(copy);
|
|
g_free(new_font);
|
|
}
|
|
|
|
/* Callbacks */
|
|
void
|
|
on_gm_world_input_view_world_text_activate(GmWorldInputView *iview, gchar *text,
|
|
GmWorldView *view) {
|
|
gm_world_process_input(view->priv->world, text);
|
|
gm_text_scroller_scroll_end(view->priv->text_scroller_world);
|
|
}
|
|
|
|
void
|
|
on_gm_world_view_world_text_received(GmWorld *world, gchar *text,
|
|
GmWorldView *view) {
|
|
gchar *inserted =
|
|
gm_world_text_view_insert(view->priv->text_view_world, text);
|
|
|
|
g_free(inserted);
|
|
}
|
|
|
|
void
|
|
on_gm_world_view_editor_save(GmEditor *editor, GmWorldView *view) {
|
|
gtk_notebook_set_current_page(GTK_NOTEBOOK(view), 0);
|
|
gtk_widget_grab_focus(GTK_WIDGET(view->priv->text_view_input));
|
|
}
|
|
|
|
void
|
|
on_gm_world_view_editor_view_close_clicked(GtkButton *button,
|
|
GtkWidget *view) {
|
|
// TODO: make this into an interface
|
|
if (GM_IS_EDITOR_VIEW(view)) {
|
|
gm_editor_close(gm_editor_view_editor(GM_EDITOR_VIEW(view)));
|
|
} else if (GM_IS_EMBEDDED_VIEW(view)) {
|
|
gm_editor_close(gm_embedded_view_editor(GM_EMBEDDED_VIEW(view)));
|
|
}
|
|
}
|
|
|
|
void
|
|
on_gm_world_view_log_view_close_clicked(GtkButton *button,
|
|
GtkWidget *view) {
|
|
gtk_widget_destroy(view);
|
|
}
|
|
|
|
|
|
void
|
|
on_gm_world_view_editor_view_modified_changed(GmEditorView *editor_view,
|
|
gboolean modified, GmWorldView *view) {
|
|
gint i, n = gtk_notebook_get_n_pages(GTK_NOTEBOOK(view));
|
|
GtkWidget *page;
|
|
GtkLabel *label;
|
|
gchar *str;
|
|
|
|
// Find this view then
|
|
for (i = 1; i < n; ++i) {
|
|
page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(view), i);
|
|
|
|
if (GM_IS_EDITOR_VIEW(page) && GM_EDITOR_VIEW(page) == editor_view) {
|
|
label = GTK_LABEL(gm_container_item(GTK_CONTAINER(
|
|
gtk_notebook_get_tab_label(GTK_NOTEBOOK(view), page)),
|
|
GTK_TYPE_LABEL));
|
|
|
|
if (!modified) {
|
|
gtk_label_set_label(label,
|
|
gm_editor_name(gm_editor_view_editor(
|
|
GM_EDITOR_VIEW(page))));
|
|
} else {
|
|
str = g_strconcat(gm_editor_name(gm_editor_view_editor(
|
|
GM_EDITOR_VIEW(page))), "*", NULL);
|
|
gtk_label_set_label(label, str);
|
|
g_free(str);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
on_gm_world_view_world_editor_added(GmWorld *world, GmEditor *editor,
|
|
GmWorldView *view) {
|
|
gm_world_view_editor_page_new(view, editor);
|
|
|
|
g_signal_connect(editor, "save",
|
|
G_CALLBACK(on_gm_world_view_editor_save), view);
|
|
}
|
|
|
|
void
|
|
on_gm_world_view_world_editor_removed(GmWorld *world, GmEditor *editor,
|
|
GmWorldView *view) {
|
|
gint i, n = gtk_notebook_get_n_pages(GTK_NOTEBOOK(view));
|
|
GtkWidget *page;
|
|
gboolean found = FALSE;
|
|
GList *ext;
|
|
|
|
for (i = 1; i < n; ++i) {
|
|
page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(view), i);
|
|
found = GM_IS_EDITOR_VIEW(page) && gm_editor_view_editor(
|
|
GM_EDITOR_VIEW(page)) == editor;
|
|
found = found || (GM_IS_EMBEDDED_VIEW(page) && gm_embedded_view_editor(
|
|
GM_EMBEDDED_VIEW(page)) == editor);
|
|
|
|
if (found) {
|
|
g_signal_handlers_disconnect_by_func(editor,
|
|
on_gm_world_view_editor_save, view);
|
|
gtk_widget_destroy(page);
|
|
|
|
gtk_notebook_set_current_page(GTK_NOTEBOOK(view), 0);
|
|
gtk_widget_grab_focus(GTK_WIDGET(view->priv->text_view_input));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
// Might be an external editor?
|
|
for (ext = view->priv->external_editors; ext; ext = ext->next) {
|
|
if ((GM_EXTERNAL_VIEW(ext->data))->editor == editor) {
|
|
g_signal_handlers_disconnect_by_func(editor,
|
|
on_gm_world_view_editor_save, view);
|
|
gm_external_view_destroy(GM_EXTERNAL_VIEW(ext->data));
|
|
|
|
view->priv->external_editors =
|
|
g_list_remove_link(view->priv->external_editors, ext);
|
|
g_list_free_1(ext);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
on_gm_world_view_world_error(GmWorld *world, gchar *text, gint code,
|
|
GmWorldView *view) {
|
|
gchar *line;
|
|
|
|
switch (code) {
|
|
case GM_NET_ERROR_CONNECTING:
|
|
line = g_strdup_printf(_("Connect failed: %s"), text);
|
|
break;
|
|
case GM_NET_ERROR_DISCONNECTED:
|
|
line = g_strdup_printf(_("Connection lost... (%s)"), text);
|
|
break;
|
|
default:
|
|
line = g_strdup_printf(_("Error: %s"), text);
|
|
break;
|
|
}
|
|
|
|
gm_world_status(world, line);
|
|
g_free(line);
|
|
}
|
|
|
|
void
|
|
on_gm_world_view_world_state_changing(GmWorld *world, guint state,
|
|
GmWorldView *view) {
|
|
gchar *line = NULL;
|
|
GmNetState pstate = gm_world_state(world);
|
|
|
|
switch (state) {
|
|
case GM_NET_STATE_TRY_ADDRESS:
|
|
line = g_strdup_printf(_("Trying %s port %s"),
|
|
gm_world_current_host(world), gm_world_current_port(world));
|
|
break;
|
|
case GM_NET_STATE_CONNECTING:
|
|
line = g_strdup_printf(_("Connecting to %s port %s"),
|
|
gm_world_current_host(world), gm_world_current_port(world));
|
|
break;
|
|
case GM_NET_STATE_CONNECTED:
|
|
line = g_strdup(_("Connected"));
|
|
break;
|
|
case GM_NET_STATE_DISCONNECTED:
|
|
if (pstate == GM_NET_STATE_CONNECTED ||
|
|
pstate == GM_NET_STATE_DISCONNECTING) {
|
|
line = g_strdup(_("Disconnected"));
|
|
}
|
|
gm_world_view_update_status(view, NULL);
|
|
break;
|
|
case GM_NET_STATE_DISCONNECTING:
|
|
line = g_strdup(_("Disconnecting"));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (line) {
|
|
gm_world_status(world, line);
|
|
g_free(line);
|
|
}
|
|
}
|
|
|
|
void
|
|
on_gm_world_view_world_active_changed(GmWorld *world, GParamSpec *pspec,
|
|
GmWorldView *view) {
|
|
if (gm_world_active(world)) {
|
|
gtk_widget_grab_focus(GTK_WIDGET(view->priv->text_view_input));
|
|
}
|
|
}
|
|
|
|
void
|
|
on_gm_world_view_world_highlight(GmWorld *world, gint start, gint end,
|
|
gchar *color, GmWorldView *view) {
|
|
GtkTextIter istart, iend;
|
|
GtkTextBuffer *buffer = gtk_text_view_get_buffer(
|
|
GTK_TEXT_VIEW(view->priv->text_view_world));
|
|
|
|
gtk_text_buffer_get_end_iter(buffer, &iend);
|
|
istart = iend;
|
|
gtk_text_iter_backward_line(&istart);
|
|
|
|
if (start != -1) {
|
|
gtk_text_iter_forward_chars(&istart, start);
|
|
iend = istart;
|
|
gtk_text_iter_forward_chars(&iend, end);
|
|
}
|
|
|
|
gtk_text_buffer_apply_tag_by_name(buffer, color, &istart, &iend);
|
|
}
|
|
|
|
|
|
gboolean on_gm_world_view_world_text_view_scroll_event(GmWorldView *view,
|
|
GdkEventScroll *event, GmWorldTextView *text) {
|
|
if (event->state & GDK_CONTROL_MASK) {
|
|
switch (event->direction) {
|
|
case GDK_SCROLL_UP:
|
|
// Decrease font size
|
|
gm_world_view_change_font_size(view, -1);
|
|
break;
|
|
case GDK_SCROLL_DOWN:
|
|
// Increase font size
|
|
gm_world_view_change_font_size(view, 1);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
on_gm_world_view_world_input_view_key_pressed(GtkWidget *widget,
|
|
GdkEventKey *event, GmWorldView *view) {
|
|
switch (event->keyval) {
|
|
case GDK_Home: case GDK_End:
|
|
if ((event->state | GDK_CONTROL_MASK) == event->state) {
|
|
if (event->keyval == GDK_End) {
|
|
gm_text_scroller_scroll_end(
|
|
view->priv->text_scroller_world);
|
|
} else {
|
|
gm_text_scroller_scroll_begin(
|
|
view->priv->text_scroller_world);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
break;
|
|
case GDK_Page_Up: case GDK_Page_Down:
|
|
if (!(event->state & GDK_CONTROL_MASK &&
|
|
!(event->state & GDK_SHIFT_MASK))) {
|
|
if (event->keyval == GDK_Page_Up) {
|
|
gm_text_scroller_scroll_page(
|
|
view->priv->text_scroller_world, -1);
|
|
} else {
|
|
gm_text_scroller_scroll_page(
|
|
view->priv->text_scroller_world, 1);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
on_gm_world_view_world_mcp_package_created(GmMcpSession *session,
|
|
GmMcpPackage *package, GmWorldView *view) {
|
|
gm_mcp_package_create_view(package, G_OBJECT(view));
|
|
}
|
|
|
|
void
|
|
on_gm_world_view_world_text_view_url_activate(GmWorldView *view,
|
|
gchar const *url) {
|
|
gm_open_url(url);
|
|
}
|