Changed src/ to gnoemoe/

This commit is contained in:
Jesse van den Kieboom 2005-11-07 09:56:25 +00:00
parent ba23ee77b1
commit a29f64288d
78 changed files with 16737 additions and 0 deletions

46
gnoemoe/Makefile.am Normal file
View File

@ -0,0 +1,46 @@
## Process this file with automake to produce Makefile.in
# SUBDIRS = test
INCLUDES = \
-DPACKAGE_DATA_DIR=\""$(datadir)"\" \
-DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \
@PACKAGE_CFLAGS@ \
@RUBYINCLUDE@
bin_PROGRAMS = gnoemoe
BUILT_SOURCES = gm-marshal.c gm-marshal.h
gnoemoe_SOURCES = $(BUILT_SOURCES) \
gm-app.c gm-app.h \
gm-options.c gm-options.h \
gm-color-table.c gm-color-table.h \
gm-net.c gm-net.h \
gm-triggers.c gm-triggers.h \
gm-world.c gm-world.h \
gm-string.c gm-string.h \
gm-support.c gm-support.h \
gm-editor.c gm-editor.h \
gm-pixbuf.c gm-pixbuf.h \
gm-debug.c gm-debug.h
include $(srcdir)/widgets/Makefile.include
include $(srcdir)/dialogs/Makefile.include
include $(srcdir)/mcp/Makefile.include
if HAVE_SCRIPT
gnoemoe_SOURCES += gm-scripts.c gm-scripts.h
endif
gm-marshal.h: gm-marshal.list $(GLIB_GENMARSHAL)
$(GLIB_GENMARSHAL) $< --header --prefix=gm_marshal > $@
gm-marshal.c: gm-marshal.list $(GLIB_GENMARSHAL)
echo "#include \"gm-marshal.h\"" > $@ && \
$(GLIB_GENMARSHAL) $< --body --prefix=gm_marshal >> $@
gnoemoe_LDADD = @PACKAGE_LIBS@ $(INTLLIBS) @RUBYLINK@
CLEANFILES = $(BUILT_SOURCES)
dist-hook:
cd $(distdir); rm -f $(BUILT_SOURCES)
AM_CFLAGS = -Werror -Wall -Wsign-compare

124
gnoemoe/ansi.h Normal file
View File

@ -0,0 +1,124 @@
#ifndef ANSI_H
#define ANSI_H
/** \defgroup ansi
* @{
*/
/** \brief enum indicating ansi code
*
* Enumeration which indicates the ansi code
*/
typedef enum _ansi_code {
A_DEFAULT = 0, /**< default (reset all attributes) */
A_BOLD = 1, /**< bold text */
A_FAINT = 2, /**< faint text */
A_ITALIC = 3, /**< italic text */
A_UNDERLINE = 4, /**< underlined text */
A_INVERSE = 7, /**< inverse foreground/background colors */
A_INVISIBLE = 8, /**< invisible text */
A_CROSSOUT = 9, /**< crossed out text */
A_DOUBLE_UNDERLINE = 21, /**< double underlined text */
A_BOLD_OFF = 22, /**< text no longer bold */
A_ITALIC_OFF = 23, /**< text no longer italic */
A_UNDERLINE_OFF = 24, /**< text no longer underlined */
A_INVERSE_OFF = 27, /**< text no longer inversed */
A_INVISIBLE_OFF = 28, /**< text no longer invisible */
A_CROSSOUT_OFF = 29, /**< text no longer crossed out */
A_FG_BLACK = 30, /**< foreground color black */
A_FG_RED = 31, /**< foreground color red */
A_FG_GREEN = 32, /**< foreground color green */
A_FG_YELLOW = 33, /**< foreground color yellow */
A_FG_BLUE = 34, /**< foreground color blue */
A_FG_PURPLE = 35, /**< foreground color purple */
A_FG_CYAN = 36, /**< foreground color cyan */
A_FG_WHITE = 37, /**< foreground color white */
A_FG_DEFAULT = 39, /**< foreground color default */
A_BG_BLACK = 40, /**< background color black */
A_BG_RED = 41, /**< background color red */
A_BG_GREEN = 42, /**< background color green */
A_BG_YELLOW = 43, /**< background color yellow */
A_BG_BLUE = 44, /**< background color blue */
A_BG_PURPLE = 45, /**< background color purple */
A_BG_CYAN = 46, /**< background color cyan */
A_BG_WHITE = 47, /**< background color white */
A_BG_DEFAULT = 49, /**< background color default */
A_NOWRAP = 50, /**< do not wrap this text */
A_FG_BLACK_H, /**< foreground color black high */
A_FG_RED_H, /**< foreground color red high */
A_FG_GREEN_H, /**< foreground color green high */
A_FG_YELLOW_H, /**< foreground color yellow high */
A_FG_BLUE_H, /**< foreground color blue high */
A_FG_PURPLE_H, /**< foreground color purple high */
A_FG_CYAN_H, /**< foreground color cyan high */
A_FG_WHITE_H, /**< foreground color white high */
A_FG_DEFAULT_H /**< foreground color default high */
} ansi_code;
/** \brief struct for containing ansi-name pair
*
* Struct can be used to create a code to name to code mapping
*/
typedef struct _ansinamepair {
const ansi_code code; /**< the ansi code */
const char *name; /**< the ansi name */
} ansinamepair;
/** \brief array containing color code/name mapping
*
* Array which can be used for color code/name mapping
*/
static const ansinamepair ansi_colors[] = {
{A_FG_BLACK, "fg_black"},
{A_FG_RED, "fg_red"},
{A_FG_GREEN, "fg_green"},
{A_FG_YELLOW, "fg_yellow"},
{A_FG_BLUE, "fg_blue"},
{A_FG_PURPLE, "fg_purple"},
{A_FG_CYAN, "fg_cyan"},
{A_FG_WHITE, "fg_white"},
{A_FG_DEFAULT, "fg_default"},
{A_FG_BLACK_H, "fg_black_h"},
{A_FG_RED_H, "fg_red_h"},
{A_FG_GREEN_H, "fg_green_h"},
{A_FG_YELLOW_H, "fg_yellow_h"},
{A_FG_BLUE_H, "fg_blue_h"},
{A_FG_PURPLE_H, "fg_purple_h"},
{A_FG_CYAN_H, "fg_cyan_h"},
{A_FG_WHITE_H, "fg_white_h"},
{A_FG_DEFAULT_H, "fg_default_h"},
{A_BG_BLACK, "bg_black"},
{A_BG_RED, "bg_red"},
{A_BG_GREEN, "bg_green"},
{A_BG_YELLOW, "bg_yellow"},
{A_BG_BLUE, "bg_blue"},
{A_BG_PURPLE, "bg_purple"},
{A_BG_CYAN, "bg_cyan"},
{A_BG_WHITE, "bg_white"},
{A_BG_DEFAULT, "bg_default"}
};
/** \brief array containing style code/name mapping
*
* Array which can be used for style code/name mapping
*/
static const ansinamepair ansi_styles[] = {
{A_BOLD, "bold"},
{A_FAINT, "faint"},
{A_BOLD_OFF, "bold-off"},
{A_UNDERLINE, "underline"},
{A_DOUBLE_UNDERLINE, "dblunderline"},
{A_UNDERLINE_OFF, "underline-off"},
{A_CROSSOUT, "crossout"},
{A_CROSSOUT_OFF, "crossout-off"},
{A_ITALIC, "italic"},
{A_ITALIC_OFF, "italic-off"}
};
/** @} */
#endif

View File

@ -0,0 +1,13 @@
## Process this file with automake to produce Makefile.in
dialogsdir = dialogs
gnoemoe_SOURCES += \
$(dialogsdir)/gm-world-info-dialog.c $(dialogsdir)/gm-world-info-dialog.h \
$(dialogsdir)/gm-world-logs-dialog.c $(dialogsdir)/gm-world-logs-dialog.h \
$(dialogsdir)/gm-world-properties-dialog.c \
$(dialogsdir)/gm-world-properties-dialog.h \
$(dialogsdir)/gm-worlds-list-dialog.c \
$(dialogsdir)/gm-worlds-list-dialog.h \
$(dialogsdir)/gm-scripts-dialog.c $(dialogsdir)/gm-scripts-dialog.h \
$(dialogsdir)/gm-preferences-dialog.c $(dialogsdir)/gm-preferences-dialog.h \
$(dialogsdir)/gm-triggers-dialog.c $(dialogsdir)/gm-triggers-dialog.h

View File

@ -0,0 +1,777 @@
#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 <stdlib.h>
#include <gtk/gtk.h>
#include <glade/glade.h>
#include <gtksourceview/gtksourceview.h>
#include <gtksourceview/gtksourcelanguage.h>
#include <gtksourceview/gtksourcelanguage.h>
#include <gtksourceview/gtksourcelanguagesmanager.h>
#include <gtksourceview/gtksourcetag.h>
#include "../gm-app.h"
#include "gm-preferences-dialog.h"
#include "../gm-support.h"
#include "../gm-debug.h"
#include "../gm-options.h"
#include "../gm-color-table.h"
void gm_preferences_dialog_run_dialog();
void on_gm_preferences_dialog_check_button_embed_editor_clicked(
GtkButton *button, gpointer user_data);
void on_gm_preferences_dialog_check_button_alt_editor_clicked(
GtkButton *button, gpointer user_data);
void on_gm_preferences_dialog_combo_box_scheme_changed(GtkComboBox *box,
gpointer user_data);
void on_gm_preferences_dialog_check_button_fg_clicked(
GtkButton *button, gpointer user_data);
void on_gm_preferences_dialog_check_button_bg_clicked(
GtkButton *button, gpointer user_data);
void on_gm_preferences_dialog_toggle_button_style_clicked(
GtkButton *button, gpointer user_data);
void on_gm_preferences_dialog_tree_view_editor_colors_row_changed(
GtkTreeSelection *selection, gpointer data);
void on_gm_preferences_dialog_color_button_fg_color_set(GtkColorButton *widget,
gpointer data);
void on_gm_preferences_dialog_color_button_bg_color_set(GtkColorButton *widget,
gpointer data);
void on_gm_preferences_dialog_response(GtkDialog *dialog, gint response,
gpointer user_data);
void gm_preferences_dialog_load_colors();
typedef struct _GmPreferencesDialog {
GladeXML *xml;
GtkWidget *dialog;
} GmPreferencesDialog;
static const GmKeyValuePair color_mapping[] = {
{"color_button_fg_default", "fg_default"},
{"color_button_fg_default_h", "fg_default_h"},
{"color_button_bg_default", "bg_default"},
{"color_button_fg_black", "fg_black"},
{"color_button_fg_black_h", "fg_black_h"},
{"color_button_fg_red", "fg_red"},
{"color_button_fg_red_h", "fg_red_h"},
{"color_button_fg_green", "fg_green"},
{"color_button_fg_green_h", "fg_green_h"},
{"color_button_fg_yellow", "fg_yellow"},
{"color_button_fg_yellow_h", "fg_yellow_h"},
{"color_button_fg_blue", "fg_blue"},
{"color_button_fg_blue_h", "fg_blue_h"},
{"color_button_fg_purple", "fg_purple"},
{"color_button_fg_purple_h", "fg_purple_h"},
{"color_button_fg_cyan", "fg_cyan"},
{"color_button_fg_cyan_h", "fg_cyan_h"},
{"color_button_fg_white", "fg_white"},
{"color_button_fg_white_h", "fg_white_h"},
{"color_button_bg_black", "bg_black"},
{"color_button_bg_red", "bg_red"},
{"color_button_bg_green", "bg_green"},
{"color_button_bg_yellow", "bg_yellow"},
{"color_button_bg_blue", "bg_blue"},
{"color_button_bg_purple", "bg_purple"},
{"color_button_bg_cyan", "bg_cyan"},
{"color_button_bg_white", "bg_white"}
};
static const GmKeyValuePair color_schemes[] = {
{N_("Default"), "default"},
{N_("White on black"), "white_on_black"},
{N_("User defined"), "user"},
{NULL, NULL}
};
static GmPreferencesDialog *preferences = NULL;
enum {
EDITOR_NAME,
EDITOR_STYLE,
EDITOR_ID,
EDITOR_N_COLUMNS
};
enum {
SCHEME_NAME,
SCHEME_OPTION,
SCHEME_N_COLUMNS
};
GtkWidget *
gm_preferences_dialog_widget(gchar *name) {
return glade_xml_get_widget(preferences->xml, name);
}
void
gm_preferences_dialog_init_combo_box_scheme() {
GtkComboBox *box = GTK_COMBO_BOX(gm_preferences_dialog_widget(
"combo_box_scheme"));
GtkListStore *store = gtk_list_store_new(SCHEME_N_COLUMNS, G_TYPE_STRING,
G_TYPE_STRING);
GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
GtkTreeIter iter;
int i, select = 0;
const gchar *scheme = gm_options_get(gm_app_options(gm_app_instance()),
"color_scheme");
gtk_cell_layout_clear(GTK_CELL_LAYOUT(box));
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(box), renderer, TRUE);
gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(box), renderer, "text",
SCHEME_NAME, NULL);
gtk_combo_box_set_model(box, GTK_TREE_MODEL(store));
i = 0;
while (color_schemes[i].key != NULL) {
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter, SCHEME_NAME, color_schemes[i].key,
SCHEME_OPTION, color_schemes[i].value, -1);
if (scheme != NULL && strcasecmp(scheme, color_schemes[i].value) == 0) {
select = i;
}
++i;
}
gtk_combo_box_set_active(box, select);
}
void
gm_preferences_dialog_init_editor_colors_tree() {
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkTreeView *tv = GTK_TREE_VIEW(
gm_preferences_dialog_widget("tree_view_editor_colors"));
GtkListStore *store = gtk_list_store_new(EDITOR_N_COLUMNS, G_TYPE_STRING,
GTK_TYPE_SOURCE_TAG_STYLE, G_TYPE_STRING);
GtkTreeModel *model =
gtk_tree_model_sort_new_with_model(GTK_TREE_MODEL(store));
GtkTreeSelection *select;
gtk_tree_view_set_model(tv, model);
select = gtk_tree_view_get_selection(tv);
g_signal_connect(select, "changed", G_CALLBACK(
on_gm_preferences_dialog_tree_view_editor_colors_row_changed),
NULL);
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes(_("Tag"), renderer,
"text", EDITOR_NAME, NULL);
gtk_tree_view_append_column(tv, column);
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes(_("Style"), renderer,
NULL);
gtk_tree_view_column_set_visible(column, FALSE);
gtk_tree_view_append_column(tv, column);
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes(_("Id"), renderer, NULL);
gtk_tree_view_column_set_visible(column, FALSE);
gtk_tree_view_append_column(tv, column);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), EDITOR_NAME,
GTK_SORT_ASCENDING);
}
void
gm_preferences_dialog_init_editor_colors() {
GtkSourceTag *st;
GtkSourceTagStyle *sty;
GSList *tags, *tag;
gchar *name, *id;
GtkTreeIter iter;
GtkListStore *store;
GtkSourceLanguage *lang = NULL;
// TODO: Fix this!
// lang = editor_get_language();
// Create column ed
gm_preferences_dialog_init_editor_colors_tree();
store = GTK_LIST_STORE(gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(
gtk_tree_view_get_model(GTK_TREE_VIEW(
gm_preferences_dialog_widget("tree_view_editor_colors"))))));
if (lang) {
tags = gtk_source_language_get_tags(lang);
for (tag = tags; tag; tag = tag->next) {
st = GTK_SOURCE_TAG(tag->data);
sty = gtk_source_tag_get_style(st);
g_object_get(G_OBJECT(st), "name", &name, "id", &id, NULL);
gtk_list_store_prepend(store, &iter);
gtk_list_store_set(store, &iter, EDITOR_NAME, name,
EDITOR_STYLE, sty, EDITOR_ID, id, -1);
g_free(name);
g_free(id);
}
g_slist_free(tags);
}
}
#define GM_PREFERENCES_DIALOG_XML \
PACKAGE_DATA_DIR "/" PACKAGE "/ui/gm-preferences.glade"
void
gm_preferences_dialog_run() {
const gchar *alt_editor;
GmOptions *options = gm_app_options(gm_app_instance());
if (preferences != NULL) {
gtk_window_present(GTK_WINDOW(preferences->dialog));
return;
} else {
preferences = g_new0(GmPreferencesDialog, 1);
}
preferences->xml = glade_xml_new(GM_PREFERENCES_DIALOG_XML,
"gm_preferences_dialog", NULL);
preferences->dialog =
gm_preferences_dialog_widget("gm_preferences_dialog");
gm_preferences_dialog_init_editor_colors();
gm_preferences_dialog_load_colors();
gm_preferences_dialog_init_combo_box_scheme();
gtk_font_button_set_font_name(GTK_FONT_BUTTON(
gm_preferences_dialog_widget("font_button_font")),
gm_options_get(options, "font-family"));
alt_editor = gm_options_get(options, "editor_alternative");
if (strcmp(alt_editor, "0") == 0) {
alt_editor = NULL;
} else {
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
gm_preferences_dialog_widget("check_button_alt_editor")),
TRUE);
gtk_entry_set_text(GTK_ENTRY(
gm_preferences_dialog_widget("entry_alt_editor")),
alt_editor);
}
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
gm_preferences_dialog_widget("check_button_embed_editor")),
gm_options_get_int(options, "editor_embed"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
gm_preferences_dialog_widget("check_button_needs_terminal")),
gm_options_get_int(options, "editor_needs_terminal"));
gtk_widget_set_sensitive(
gm_preferences_dialog_widget("entry_alt_editor"),
alt_editor != NULL);
gtk_widget_set_sensitive(
gm_preferences_dialog_widget("check_button_embed_editor"),
alt_editor != NULL);
gtk_widget_set_sensitive(
gm_preferences_dialog_widget("check_button_needs_terminal"),
alt_editor != NULL
&& gm_options_get_int(options, "editor_embed") == 0);
glade_xml_signal_connect(preferences->xml,
"on_check_button_alt_editor_clicked", G_CALLBACK(
on_gm_preferences_dialog_check_button_alt_editor_clicked));
glade_xml_signal_connect(preferences->xml,
"on_check_button_embed_editor_clicked", G_CALLBACK(
on_gm_preferences_dialog_check_button_embed_editor_clicked));
glade_xml_signal_connect(preferences->xml,
"on_check_button_fg_clicked", G_CALLBACK(
on_gm_preferences_dialog_check_button_fg_clicked));
glade_xml_signal_connect(preferences->xml,
"on_check_button_bg_clicked", G_CALLBACK(
on_gm_preferences_dialog_check_button_bg_clicked));
glade_xml_signal_connect(preferences->xml,
"on_combo_box_scheme_changed", G_CALLBACK(
on_gm_preferences_dialog_combo_box_scheme_changed));
glade_xml_signal_connect_data(preferences->xml,
"on_toggle_button_bold_clicked", G_CALLBACK(
on_gm_preferences_dialog_toggle_button_style_clicked),
GINT_TO_POINTER(1));
glade_xml_signal_connect_data(preferences->xml,
"on_toggle_button_style_clicked", G_CALLBACK(
on_gm_preferences_dialog_toggle_button_style_clicked),
GINT_TO_POINTER(2));
glade_xml_signal_connect_data(preferences->xml,
"on_toggle_button_underline_clicked", G_CALLBACK(
on_gm_preferences_dialog_toggle_button_style_clicked),
GINT_TO_POINTER(3));
glade_xml_signal_connect_data(preferences->xml,
"on_toggle_button_strike_trough_clicked", G_CALLBACK(
on_gm_preferences_dialog_toggle_button_style_clicked),
GINT_TO_POINTER(4));
glade_xml_signal_connect(preferences->xml,
"on_color_button_fg_color_set", G_CALLBACK(
on_gm_preferences_dialog_color_button_fg_color_set));
glade_xml_signal_connect(preferences->xml,
"on_color_button_bg_color_set", G_CALLBACK(
on_gm_preferences_dialog_color_button_bg_color_set));
gm_preferences_dialog_run_dialog();
}
void
gm_preferences_dialog_load_colors() {
int i;
GtkColorButton *but;
GdkColor col;
GmColorTable *color_table = gm_app_color_table(gm_app_instance());
for (i = 0; i < (int)(sizeof(color_mapping) / sizeof(GmKeyValuePair)); i++) {
but = GTK_COLOR_BUTTON(gm_preferences_dialog_widget(
color_mapping[i].key));
gm_color_table_get(color_table, color_mapping[i].value, &col);
gtk_color_button_set_color(but, &col);
}
}
void
gm_preferences_dialog_save_colors() {
int i;
GtkColorButton *but;
GdkColor col;
GmColorTable *color_table = gm_app_color_table(gm_app_instance());
gchar *col_str;
for (i = 0; i < (int)(sizeof(color_mapping) / sizeof(GmKeyValuePair)); i++) {
but = GTK_COLOR_BUTTON(gm_preferences_dialog_widget(
color_mapping[i].key));
gtk_color_button_get_color(but, &col);
col_str = g_strdup_printf("#%04X%04X%04X", col.red, col.green,
col.blue);
gm_color_table_set(color_table, color_mapping[i].value, col_str);
g_free(col_str);
}
}
void
gm_preferences_dialog_save_editor_color(GtkTreeModel *model,
GtkTreeIter *iter) {
gchar *name, *id, *opt_key, *opt_value, *fg, *bg;
GtkSourceTagStyle *style;
GmOptions *options = gm_app_options(gm_app_instance());
gtk_tree_model_get(model, iter, EDITOR_NAME, &name, EDITOR_STYLE, &style,
EDITOR_ID, &id, -1);
opt_key = g_strconcat("editor_", name, NULL);
if (!(style->mask & GTK_SOURCE_TAG_STYLE_USE_FOREGROUND)) {
fg = g_strdup("0");
} else {
fg = g_strdup_printf("#%04X%04X%04X", style->foreground.red,
style->foreground.green, style->foreground.blue);
}
if (!(style->mask & GTK_SOURCE_TAG_STYLE_USE_BACKGROUND)) {
bg = g_strdup("0");
} else {
bg = g_strdup_printf("#%04X%04X%04X", style->background.red,
style->background.green, style->background.blue);
}
opt_value = g_strdup_printf("%s,%s,%d,%d,%d,%d", fg, bg, style->bold,
style->italic, style->underline,
style->strikethrough);
gm_debug_msg(DEBUG_DEFAULT, "GmPreferencesDialog.SaveEditorColor %s to %s", opt_key,
opt_value);
gm_options_set(options, opt_key, opt_value);
// TODO: do something about this
//gtk_source_language_set_tag_style(editor_get_language(), id, style);
g_free(opt_key);
g_free(opt_value);
g_free(id);
g_free(fg);
g_free(bg);
g_free(name);
// CHECK this:
//gtk_source_tag_style_free(style);
}
void
gm_preferences_dialog_save_editor_colors() {
GtkTreeIter iter;
GtkTreeView *vw = GTK_TREE_VIEW(
gm_preferences_dialog_widget("tree_view_editor_colors"));
GtkTreeModelSort *model = GTK_TREE_MODEL_SORT(gtk_tree_view_get_model(vw));
GtkTreeModel *store = gtk_tree_model_sort_get_model(model);
if (gtk_tree_model_get_iter_first(store, &iter)) {
do {
gm_preferences_dialog_save_editor_color(store, &iter);
} while (gtk_tree_model_iter_next(store, &iter));
}
}
gboolean
gm_preferences_dialog_check_values() {
const gchar *alt_editor = gtk_entry_get_text(GTK_ENTRY(
gm_preferences_dialog_widget("entry_alt_editor")));
const gchar *font_description = gtk_font_button_get_font_name(
GTK_FONT_BUTTON(gm_preferences_dialog_widget("font_button_font")));
gboolean use_alt_editor = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
gm_preferences_dialog_widget("check_button_alt_editor")));
GmOptions *options = gm_app_options(gm_app_instance());
if (use_alt_editor && alt_editor[0] != '\0') {
gm_error_dialog(_("Editor can't be empty, enter a non empty command"),
GTK_WINDOW(preferences->dialog));
gm_notebook_focus_from_label(GTK_NOTEBOOK(gm_preferences_dialog_widget(
"notebook_preferences")), _("Editor"));
gtk_widget_grab_focus(gm_preferences_dialog_widget("entry_alt_editor"));
return FALSE;
}
if (use_alt_editor) {
gm_options_set(options, "editor_alternative", alt_editor);
} else {
gm_options_set(options, "editor_alternative", "0");
}
gm_options_set_int(options, "editor_embed", gtk_toggle_button_get_active(
GTK_TOGGLE_BUTTON(gm_preferences_dialog_widget
("check_button_embed_editor"))));
gm_options_set_int(options, "editor_needs_terminal",
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
gm_preferences_dialog_widget("check_button_needs_terminal"))));
gm_options_set(options, "font-family", font_description);
gm_preferences_dialog_save_colors();
gm_preferences_dialog_save_editor_colors();
return TRUE;
}
void
gm_preferences_dialog_run_dialog() {
g_signal_connect(preferences->dialog, "response",
G_CALLBACK(on_gm_preferences_dialog_response), NULL);
gtk_widget_show(GTK_WIDGET(preferences->dialog));
}
/* CALLBACKS */
void
on_gm_preferences_dialog_response(GtkDialog *dialog, gint response,
gpointer user_data) {
gboolean is_okay = TRUE;
switch (response) {
case GTK_RESPONSE_OK: case GTK_RESPONSE_APPLY:
if (gm_preferences_dialog_check_values()) {
gm_options_save(gm_app_options(gm_app_instance()));
if (response == GTK_RESPONSE_APPLY) {
is_okay = FALSE;
}
} else {
is_okay = FALSE;
}
break;
default:
break;
}
if (is_okay) {
gtk_widget_destroy(GTK_WIDGET(dialog));
g_object_unref(preferences->xml);
g_free(preferences);
preferences = NULL;
}
}
void
on_gm_preferences_dialog_check_button_embed_editor_clicked(
GtkButton *button, gpointer user_data) {
gboolean active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
GmOptions *options = gm_app_options(gm_app_instance());
if (active) {
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
gm_preferences_dialog_widget("check_button_needs_terminal")),
TRUE);
} else {
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
gm_preferences_dialog_widget("check_button_needs_terminal")),
gm_options_get_int(options, "editor_needs_terminal"));
}
gtk_widget_set_sensitive(gm_preferences_dialog_widget(
"check_button_needs_terminal"), !active);
}
void
on_gm_preferences_dialog_check_button_alt_editor_clicked(
GtkButton *button, gpointer user_data) {
gboolean active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
if (!active) {
gtk_entry_set_text(GTK_ENTRY(gm_preferences_dialog_widget(
"entry_alt_editor")), "");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
gm_preferences_dialog_widget("check_button_embed_editor")),
FALSE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
gm_preferences_dialog_widget("check_button_needs_terminal")),
FALSE);
}
gtk_widget_set_sensitive(gm_preferences_dialog_widget(
"entry_alt_editor"), active);
gtk_widget_set_sensitive(gm_preferences_dialog_widget(
"check_button_embed_editor"), active);
gtk_widget_set_sensitive(gm_preferences_dialog_widget(
"check_button_needs_terminal"), active);
}
void
gm_preferences_dialog_editor_style_do_enable() {
gtk_widget_set_sensitive(gm_preferences_dialog_widget("check_button_fg"),
TRUE);
gtk_widget_set_sensitive(gm_preferences_dialog_widget("check_button_bg"),
TRUE);
gtk_widget_set_sensitive(gm_preferences_dialog_widget("toggle_button_bold"),
TRUE);
gtk_widget_set_sensitive(gm_preferences_dialog_widget(
"toggle_button_style"), TRUE);
gtk_widget_set_sensitive(gm_preferences_dialog_widget(
"toggle_button_underline"), TRUE);
gtk_widget_set_sensitive(gm_preferences_dialog_widget(
"toggle_button_strike_through"), TRUE);
}
void
on_gm_preferences_dialog_tree_view_editor_colors_row_changed(
GtkTreeSelection *selection, gpointer data) {
GtkTreeIter iter;
GtkTreeModel *model;
GtkSourceTagStyle *style;
GtkColorButton *fg, *bg;
GtkToggleButton *fgt, *bgt;
GdkColor empty_col;
gdk_color_parse("black", &empty_col);
gtk_tree_selection_get_selected(selection, &model, &iter);
gtk_tree_model_get(model, &iter, EDITOR_STYLE, &style, -1);
fg = GTK_COLOR_BUTTON(gm_preferences_dialog_widget("color_button_fg"));
bg = GTK_COLOR_BUTTON(gm_preferences_dialog_widget("color_button_bg"));
fgt = GTK_TOGGLE_BUTTON(gm_preferences_dialog_widget("check_button_fg"));
bgt = GTK_TOGGLE_BUTTON(gm_preferences_dialog_widget("check_button_bg"));
if (style->mask & GTK_SOURCE_TAG_STYLE_USE_FOREGROUND) {
gtk_color_button_set_color(fg, &(style->foreground));
gtk_widget_set_sensitive(GTK_WIDGET(fg), TRUE);
gtk_toggle_button_set_active(fgt, TRUE);
} else {
gtk_color_button_set_color(fg, &empty_col);
gtk_widget_set_sensitive(GTK_WIDGET(fg), FALSE);
gtk_toggle_button_set_active(fgt, FALSE);
}
if (style->mask & GTK_SOURCE_TAG_STYLE_USE_BACKGROUND) {
gtk_color_button_set_color(bg, &(style->background));
gtk_widget_set_sensitive(GTK_WIDGET(bg), TRUE);
gtk_toggle_button_set_active(bgt, TRUE);
} else {
gtk_color_button_set_color(bg, &empty_col);
gtk_widget_set_sensitive(GTK_WIDGET(bg), FALSE);
gtk_toggle_button_set_active(bgt, FALSE);
}
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gm_preferences_dialog_widget(
"check_button_bold")), style->bold);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gm_preferences_dialog_widget(
"check_button_style")), style->italic);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gm_preferences_dialog_widget(
"check_button_underline")), style->underline);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gm_preferences_dialog_widget(
"check_button_strike_through")), style->strikethrough);
gm_preferences_dialog_editor_style_do_enable();
}
typedef struct _GmSelectionInfo {
GtkTreeIter siter;
GtkTreeIter iter;
GtkListStore *store;
GtkTreeModel *model;
GtkSourceTagStyle *style;
} GmSelectionInfo;
GmSelectionInfo
gm_preferences_dialog_get_selection_info() {
GtkTreeSelection *selection;
GmSelectionInfo info;
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
gm_preferences_dialog_widget("tree_view_editor_colors")));
gtk_tree_selection_get_selected(selection, &(info.model), &(info.siter));
gtk_tree_model_get(info.model, &(info.siter), EDITOR_STYLE, &(info.style),
-1);
gtk_tree_model_sort_convert_iter_to_child_iter(GTK_TREE_MODEL_SORT(
info.model), &(info.iter), &(info.siter));
info.store = GTK_LIST_STORE(gtk_tree_model_sort_get_model(
GTK_TREE_MODEL_SORT(info.model)));
return info;
}
void
on_gm_preferences_dialog_check_button_fg_clicked(GtkButton *button,
gpointer user_data) {
GmSelectionInfo info;
gboolean tog = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
GtkWidget *clb = gm_preferences_dialog_widget("color_button_fg");
GdkColor empty_col;
info = gm_preferences_dialog_get_selection_info();
gdk_color_parse("black", &empty_col);
if (tog) {
info.style->mask = info.style->mask |
GTK_SOURCE_TAG_STYLE_USE_FOREGROUND;
} else {
info.style->mask = info.style->mask &
~GTK_SOURCE_TAG_STYLE_USE_FOREGROUND;
gtk_color_button_set_color(GTK_COLOR_BUTTON(clb), &empty_col);
info.style->foreground = empty_col;
}
gtk_list_store_set(info.store, &(info.iter), EDITOR_STYLE, info.style, -1);
gtk_source_tag_style_free(info.style);
gtk_widget_set_sensitive(clb, tog);
}
void
on_gm_preferences_dialog_check_button_bg_clicked(GtkButton *button,
gpointer user_data) {
GmSelectionInfo info;
gboolean tog = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
GtkWidget *clb = gm_preferences_dialog_widget("color_button_bg");
GdkColor empty_col;
info = gm_preferences_dialog_get_selection_info();
gdk_color_parse("black", &empty_col);
if (tog) {
info.style->mask = info.style->mask |
GTK_SOURCE_TAG_STYLE_USE_BACKGROUND;
} else {
info.style->mask = info.style->mask &
~GTK_SOURCE_TAG_STYLE_USE_BACKGROUND;
gtk_color_button_set_color(GTK_COLOR_BUTTON(clb), &empty_col);
info.style->background = empty_col;
}
gtk_list_store_set(info.store, &(info.iter), EDITOR_STYLE, info.style, -1);
gtk_source_tag_style_free(info.style);
gtk_widget_set_sensitive(clb, tog);
}
void
on_gm_preferences_dialog_toggle_button_style_clicked(GtkButton *button,
gpointer user_data) {
GmSelectionInfo info;
int type = GPOINTER_TO_INT(user_data);
gboolean tog = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
info = gm_preferences_dialog_get_selection_info();
switch (type) {
case 1: /* bold */
info.style->bold = tog;
break;
case 2: /* italic */
info.style->italic = tog;
break;
case 3: /* underline */
info.style->underline = tog;
break;
case 4:
info.style->strikethrough = tog;
break;
}
gtk_list_store_set(info.store, &(info.iter), EDITOR_STYLE, info.style, -1);
gtk_source_tag_style_free(info.style);
}
void
on_gm_preferences_dialog_color_button_fg_color_set(GtkColorButton *widget,
gpointer user_data) {
GmSelectionInfo info;
info = gm_preferences_dialog_get_selection_info();
gtk_color_button_get_color(widget, &(info.style->foreground));
gtk_list_store_set(info.store, &(info.iter), EDITOR_STYLE, info.style, -1);
gtk_source_tag_style_free(info.style);
}
void
on_gm_preferences_dialog_color_button_bg_color_set(GtkColorButton *widget,
gpointer user_data) {
GmSelectionInfo info;
info = gm_preferences_dialog_get_selection_info();
gtk_color_button_get_color(widget, &(info.style->background));
gtk_list_store_set(info.store, &(info.iter), EDITOR_STYLE, info.style, -1);
gtk_source_tag_style_free(info.style);
}
void
on_gm_preferences_dialog_combo_box_scheme_changed(GtkComboBox *box,
gpointer user_data) {
GtkTreeIter iter;
gchar *scheme;
GtkTreeModel *model = gtk_combo_box_get_model(box);
GmColorTable *color_table = gm_app_color_table(gm_app_instance());
gtk_combo_box_get_active_iter(box, &iter);
gtk_tree_model_get(model, &iter, SCHEME_OPTION, &scheme, -1);
gm_color_table_set_from_scheme_name(color_table, scheme);
g_free(scheme);
}

View File

@ -0,0 +1,6 @@
#ifndef __GM_PREFERENCES_DIALOG_H__
#define __GM_PREFERENCES_DIALOG_H__
void gm_preferences_dialog_run();
#endif /* __GM_PREFERENCES_DIALOG_H__ */

View File

@ -0,0 +1,920 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_RUBY
#include <gtk/gtk.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <gtksourceview/gtksourceview.h>
#include <gtksourceview/gtksourcelanguage.h>
#include <gtksourceview/gtksourcelanguagesmanager.h>
#include <gtksourceview/gtksourcetag.h>
#include <libgnomevfs/gnome-vfs.h>
#include <glade/glade.h>
#include "../gm-scripts.h"
#include "../gm-world.h"
#include "../gm-debug.h"
#include "../gm-app.h"
#include "../gm-support.h"
#include "../widgets/gm-app-view.h"
#include "../widgets/gm-text-scroller.h"
#define SCRIPT_TEMPLATE \
_("=begin\n" \
"\t<title>\n" \
"\t<copyright>\n" \
"\n\tRegister functions in the register_functions method defined below.\n" \
"\tRegister functions with " \
"$scripts.register(<name>, <description>[, <alias>])\n=end" \
"\n\ndef register_functions\n" \
"\t$scripts.register(\"myscript\", \"use /myscript\")\nend\n\n" \
"def myscript(argstr)\n\t# Insert code here\nend\n")
void on_gm_scripts_dialog_script_added(GmScripts *scripts, GmScript *script,
gpointer user_data);
void on_gm_scripts_dialog_script_changed(GmScripts *scripts, GmScript *script,
gpointer user_data);
void on_gm_scripts_dialog_script_removed(GmScripts *scripts, GmScript *script,
gpointer user_data);
void on_gm_scripts_dialog_message(GmScripts *scripts, gchar *message,
gpointer user_data);
void on_gm_scripts_dialog_error(GmScripts *scripts, gchar *message,
gpointer user_data);
void on_gm_scripts_dialog_run(GmScripts *scripts, gchar *message,
gpointer user_data);
typedef enum _MessageType {
SCRIPT_MESSAGE,
SCRIPT_ERROR,
SCRIPT_RUN
} MessageType;
typedef enum _scripts_columns {
SCRIPTS_NAME,
SCRIPTS_OBJECT,
SCRIPTS_N
} scripts_columns;
typedef struct _SelectionInfo {
GtkTreeView *view;
GtkTreeModel *model;
GtkTreeIter seliter;
GtkTreeIter parent;
gboolean has_parent;
gboolean can_save;
gchar *filename;
} SelectionInfo;
typedef struct _GmScriptsDialog {
GtkTextTagTable *tag_table;
GladeXML *xml;
GtkTextBuffer *text_buffer_console;
gchar *current_edit;
GtkTextBuffer *text_buffer_editor;
GtkWidget *dialog;
GtkWidget *text_view_console;
GtkWidget *statusbar_editor;
GtkWidget *button_save;
GtkWidget *tree_view_scripts;
GtkWidget *tree_view_files;
GtkWidget *button_delete;
GmTextScroller *text_scroller;
SelectionInfo info;
} GmScriptsDialog;
static GmScriptsDialog *scripts_dialog = NULL;
#define G_SCRIPTS_XML PACKAGE_DATA_DIR "/" PACKAGE "/ui/gm-scripts.glade"
gboolean on_gm_scripts_dialog_delete(GtkWidget *widget, GdkEvent *event,
gpointer user_data);
void on_files_changed(GtkTreeSelection *treeselection, gpointer user_data);
void on_tree_view_files_row_activated(GtkTreeView *treeview, GtkTreePath *arg1,
GtkTreeViewColumn *arg2, gpointer user_data);
void on_text_buffer_editor_modified_changed(GtkTextBuffer *buffer,
gpointer user_data);
void on_tool_button_new_clicked(GtkToolButton *button, gpointer user_data);
void on_tool_button_save_clicked(GtkToolButton *button, gpointer user_data);
void on_tool_button_save_as_clicked(GtkToolButton *button, gpointer user_data);
void on_tool_button_delete_clicked(GtkToolButton *button, gpointer user_data);
gboolean
gm_scripts_dialog_can_write(gchar *filename) {
return (access(filename, W_OK) == 0);
}
void
gm_scripts_dialog_set_status(gchar *msg) {
gtk_statusbar_pop(GTK_STATUSBAR(scripts_dialog->statusbar_editor),
0);
gtk_statusbar_push(GTK_STATUSBAR(scripts_dialog->statusbar_editor),
0, msg);
}
gboolean
gm_scripts_dialog_editor_save(gchar *filename) {
FILE *f;
gchar *msg, *text;
GtkTextIter start, end;
if (!filename) {
return FALSE;
}
f = fopen(filename, "w");
if (f) {
gtk_text_buffer_get_start_iter(scripts_dialog->text_buffer_editor,
&start);
gtk_text_buffer_get_end_iter(scripts_dialog->text_buffer_editor, &end);
text = gtk_text_buffer_get_text(scripts_dialog->text_buffer_editor,
&start, &end, TRUE);
fputs(text, f);
msg = g_strconcat(_("Saved "), filename, NULL);
gm_scripts_dialog_set_status(msg);
g_free(msg);
if (scripts_dialog->current_edit != filename) {
g_free(scripts_dialog->current_edit);
scripts_dialog->current_edit = g_strdup(filename);
}
gtk_text_buffer_set_modified(scripts_dialog->text_buffer_editor,
FALSE);
fclose(f);
return TRUE;
} else {
text = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
msg = g_strconcat(_("Saving failed: "), strerror(errno), " (",
text, ")", NULL);
gm_error_dialog(msg, GTK_WINDOW(scripts_dialog->dialog));
g_free(text);
g_free(msg);
return FALSE;
}
}
void
gm_scripts_dialog_editor_load(SelectionInfo *info) {
FILE *f;
gchar line[1024];
GtkTextIter end;
gchar *lline, *msg;
if (info) {
gtk_text_buffer_set_text(scripts_dialog->text_buffer_editor, "", 0);
gtk_text_buffer_get_end_iter(scripts_dialog->text_buffer_editor, &end);
f = fopen(info->filename, "r");
if (f) {
while (fgets((char *) &line, 1024 - 1, f) != NULL) {
lline = g_locale_to_utf8((gchar *)&line, strlen((char *)&line),
NULL, NULL, NULL);
if (lline) {
gtk_text_buffer_insert(scripts_dialog->text_buffer_editor,
&end, lline, strlen(lline));
g_free(lline);
} else {
gtk_text_buffer_insert(scripts_dialog->text_buffer_editor,
&end, (gchar *)&line, strlen((char *)&line));
}
}
fclose(f);
gtk_widget_set_sensitive(scripts_dialog->button_save,
info->can_save);
g_free(scripts_dialog->current_edit);
scripts_dialog->current_edit = g_strdup(info->filename);
g_free(scripts_dialog->current_edit);
scripts_dialog->current_edit = g_strdup(info->filename);
msg = g_strconcat(_("Loaded "), info->filename, NULL);
gm_scripts_dialog_set_status(msg);
g_free(msg);
} else {
gm_debug_msg(DEBUG_DEFAULT, "GmScript.EditorLoad: file (%s) could not be read: %s",
scripts_dialog->current_edit, strerror(errno));
}
} else {
gtk_text_buffer_set_text(scripts_dialog->text_buffer_editor,
SCRIPT_TEMPLATE, -1);
g_free(scripts_dialog->current_edit);
scripts_dialog->current_edit = NULL;
gm_scripts_dialog_set_status(_("New <untitled>"));
}
}
void
gm_scripts_dialog_init_console() {
GtkTextView *txt = GTK_TEXT_VIEW(scripts_dialog->text_view_console);
PangoFontDescription *f;
gtk_text_view_set_buffer(txt, scripts_dialog->text_buffer_console);
gtk_text_view_set_editable(txt, FALSE);
f =
pango_font_description_from_string("monospace 8");
if (f) {
gtk_widget_modify_font(GTK_WIDGET(txt), f);
pango_font_description_free(f);
}
}
void
gm_scripts_dialog_tree_add_function(GtkTreeStore *model, GtkTreeIter *parent,
GmScriptFunction *fi) {
GtkTreeIter item;
gchar *name, *description, *all, *markup;
gtk_tree_store_append(model, &item, parent);
name = g_filename_to_utf8(fi->name, strlen(fi->name), NULL,
NULL, NULL);
description = g_locale_to_utf8(fi->description, strlen(fi->description),
NULL, NULL, NULL);
markup = g_markup_escape_text(description, g_utf8_strlen(description, -1));
all = g_strconcat("<b>", name, "</b>\n", markup, NULL);
gtk_tree_store_set(model, &item, SCRIPTS_NAME, all, -1);
g_free(all);
g_free(markup);
g_free(description);
g_free(name);
}
void
gm_scripts_dialog_tree_update_script_item(GmScript *script, GtkTreeIter *iter) {
GList *list;
GtkTreeStore *model = GTK_TREE_STORE(gtk_tree_view_get_model(
GTK_TREE_VIEW(scripts_dialog->tree_view_scripts)));
GtkTreeIter item;
if (gtk_tree_model_iter_children(GTK_TREE_MODEL(model), &item, iter)) {
while (gtk_tree_store_remove(model, &item));
}
for (list = script->functions; list; list = list->next) {
gm_scripts_dialog_tree_add_function(model, iter,
(GmScriptFunction *)(list->data));
}
}
gboolean
gm_scripts_dialog_model_find_script(GtkTreeModel *model,
GmScript *script, GtkTreeIter *item, GtkTreeIter *parent) {
GmScript *obj;
if (gtk_tree_model_iter_children(model, item, parent)) {
do {
gtk_tree_model_get(model, item, SCRIPTS_OBJECT,
&obj, -1);
if (obj == script) {
return TRUE;
}
} while (gtk_tree_model_iter_next(model, item));
}
return FALSE;
}
void
gm_scripts_dialog_tree_update_script(GmScript *script) {
GtkTreeModel *model = gtk_tree_view_get_model(
GTK_TREE_VIEW(scripts_dialog->tree_view_scripts));
GtkTreeIter item;
if (gm_scripts_dialog_model_find_script(model, script, &item, NULL)) {
gm_scripts_dialog_tree_update_script_item(script, &item);
}
}
void
gm_scripts_dialog_tree_remove_script(GmScript *script) {
GtkTreeModel *model = gtk_tree_view_get_model(
GTK_TREE_VIEW(scripts_dialog->tree_view_scripts));
GtkTreeIter item;
if (gm_scripts_dialog_model_find_script(model, script, &item, NULL)) {
gtk_tree_store_remove(GTK_TREE_STORE(model), &item);
}
}
void
gm_scripts_dialog_tree_add_script(GmScript *script) {
GtkTreeStore *model = GTK_TREE_STORE(gtk_tree_view_get_model(
GTK_TREE_VIEW(scripts_dialog->tree_view_scripts)));
GtkTreeIter item;
gchar *name, *base;
gtk_tree_store_append(model, &item, NULL);
name = g_filename_to_utf8(script->filename, strlen(script->filename), NULL,
NULL, NULL);
base = g_path_get_basename(name);
g_free(name);
if (script->type == GM_SCRIPT_TYPE_USER) {
name = g_strconcat(_("<b>User:</b> "), base, NULL);
} else {
name = g_strconcat(_("<b>Share:</b> "), base, NULL);
}
g_free(base);
gtk_tree_store_set(model, &item, SCRIPTS_NAME, name, SCRIPTS_OBJECT,
script, -1);
g_free(name);
gm_scripts_dialog_tree_update_script_item(script, &item);
}
void
gm_scripts_dialog_fill_tree() {
GtkTreeView *view = GTK_TREE_VIEW(scripts_dialog->tree_view_scripts);
GtkTreeStore *model = GTK_TREE_STORE(gtk_tree_view_get_model(view));
GList *scripts;
gtk_tree_store_clear(model);
for (scripts = gm_scripts_scripts(gm_app_scripts(gm_app_instance()));
scripts; scripts = scripts->next) {
gm_scripts_dialog_tree_add_script((GmScript *)(scripts->data));
}
}
void
gm_scripts_dialog_init_tree() {
GtkTreeView *view = GTK_TREE_VIEW(scripts_dialog->tree_view_scripts);
GtkTreeStore *model = gtk_tree_store_new(SCRIPTS_N, G_TYPE_STRING,
G_TYPE_POINTER);
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes(_("Scripts"), renderer,
"markup", SCRIPTS_NAME, NULL);
gtk_tree_view_append_column(view, column);
gtk_tree_view_set_model(view, GTK_TREE_MODEL(model));
gm_scripts_dialog_fill_tree();
}
void
gm_scripts_dialog_remove_file(GmScript *script) {
GtkTreeView *view = GTK_TREE_VIEW(scripts_dialog->tree_view_files);
GtkTreeStore *model = GTK_TREE_STORE(gtk_tree_view_get_model(view));
const gchar *sharedir = PACKAGE_DATA_DIR "/" PACKAGE "/scripts";
GtkTreeIter parent;
GtkTreeIter item;
gchar *name = g_filename_to_utf8(script->filename,
strlen(script->filename), NULL, NULL, NULL);
if (strncmp(sharedir, name, strlen(sharedir)) == 0) {
gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(model), &parent, "0");
} else {
gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(model), &parent, "1");
}
g_free(name);
if (gm_scripts_dialog_model_find_script(GTK_TREE_MODEL(model), script,
&item, &parent)) {
gtk_tree_store_remove(model, &item);
}
}
gboolean
gm_scripts_dialog_add_file(GmScript *script) {
GtkTreeView *view = GTK_TREE_VIEW(scripts_dialog->tree_view_files);
GtkTreeStore *model = GTK_TREE_STORE(gtk_tree_view_get_model(view));
GtkTreeIter share, home, parent, item;
gchar *sharedir = g_strdup(PACKAGE_DATA_DIR "/" PACKAGE "/scripts");
gchar *homedir = g_strconcat(gm_app_path(gm_app_instance()), "/scripts",
NULL);
gchar *name;
gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(model), &share, "0");
gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(model), &home, "1");
name = g_filename_to_utf8(script->filename, strlen(script->filename), NULL,
NULL, NULL);
if (strncmp(sharedir, name, strlen(sharedir)) == 0) {
parent = share;
} else if (strncmp(homedir, name, strlen(homedir)) == 0) {
parent = home;
} else {
g_free(sharedir);
g_free(homedir);
g_free(name);
return FALSE;
}
gtk_tree_store_append(model, &item, &parent);
gtk_tree_store_set(model, &item, SCRIPTS_NAME, g_strrstr(name, "/") + 1,
SCRIPTS_OBJECT, script, -1);
g_free(name);
g_free(sharedir);
g_free(homedir);
return TRUE;
}
void
gm_scripts_dialog_fill_files() {
GtkTreeView *view = GTK_TREE_VIEW(scripts_dialog->tree_view_files);
GtkTreeStore *model = GTK_TREE_STORE(gtk_tree_view_get_model(view));
GtkTreeIter share, home;
GtkTreePath *path;
GList *scripts;
GmScript *script;
gtk_tree_store_clear(model);
gtk_tree_store_append(model, &share, NULL);
gtk_tree_store_set(model, &share, SCRIPTS_NAME, _("Share"), -1);
gtk_tree_store_append(model, &home, NULL);
gtk_tree_store_set(model, &home, SCRIPTS_NAME, _("User"), -1);
for (scripts = gm_scripts_scripts(gm_app_scripts(gm_app_instance()));
scripts; scripts = scripts->next) {
script = (GmScript *)(scripts->data);
gm_scripts_dialog_add_file(script);
}
path = gtk_tree_model_get_path(GTK_TREE_MODEL(model), &home);
gtk_tree_view_expand_to_path(view, path);
gtk_tree_path_free(path);
path = gtk_tree_model_get_path(GTK_TREE_MODEL(model), &share);
gtk_tree_view_expand_to_path(view, path);
gtk_tree_path_free(path);
}
void
gm_scripts_dialog_init_files() {
GtkTreeView *view = GTK_TREE_VIEW(scripts_dialog->tree_view_files);
GtkTreeStore *model = gtk_tree_store_new(SCRIPTS_N, G_TYPE_STRING,
G_TYPE_POINTER);
GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
GtkTreeSelection *selection;
GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(
_("Filename"), renderer, "text", SCRIPTS_NAME, NULL);
gtk_tree_view_append_column(view, column);
gtk_tree_view_set_model(view, GTK_TREE_MODEL(model));
selection = gtk_tree_view_get_selection(view);
g_signal_connect((gpointer)selection, "changed",
G_CALLBACK(on_files_changed), NULL);
gm_scripts_dialog_fill_files();
}
GtkWidget *
gm_scripts_dialog_init_editor() {
GtkSourceLanguagesManager *lm = gtk_source_languages_manager_new();
GtkSourceLanguage *lang;
GtkWidget *view;
PangoFontDescription *f;
lang = gtk_source_languages_manager_get_language_from_mime_type(lm,
"application/x-ruby");
view = gtk_source_view_new();
scripts_dialog->text_buffer_editor = gtk_text_view_get_buffer(
GTK_TEXT_VIEW(view));
g_signal_connect((gpointer)scripts_dialog->text_buffer_editor,
"modified-changed",
G_CALLBACK(on_text_buffer_editor_modified_changed), NULL);
if (lang) {
gtk_source_buffer_set_language(
GTK_SOURCE_BUFFER(scripts_dialog->text_buffer_editor), lang);
}
f = pango_font_description_from_string(gm_options_get(gm_app_options(
gm_app_instance()), "font-family"));
gtk_widget_modify_font(view, f);
gtk_source_view_set_insert_spaces_instead_of_tabs(GTK_SOURCE_VIEW(view),
FALSE);
gtk_source_view_set_auto_indent(GTK_SOURCE_VIEW(view), TRUE);
gtk_source_view_set_show_line_numbers(GTK_SOURCE_VIEW(view), TRUE);
gtk_source_view_set_smart_home_end(GTK_SOURCE_VIEW(view), TRUE);
gtk_source_view_set_tabs_width(GTK_SOURCE_VIEW(view), 4);
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD_CHAR);
gtk_text_view_set_left_margin(GTK_TEXT_VIEW(view), 6);
gtk_text_view_set_right_margin(GTK_TEXT_VIEW(view), 6);
gtk_source_buffer_set_highlight(GTK_SOURCE_BUFFER(
scripts_dialog->text_buffer_editor), TRUE);
// Load default template
gtk_text_buffer_set_text(scripts_dialog->text_buffer_editor,
SCRIPT_TEMPLATE, -1);
gtk_widget_show(view);
return view;
}
GtkTextTag *
gm_scripts_dialog_create_tag(gchar *name, gchar *fg) {
GtkTextTag *tag;
GdkColor col;
gdk_color_parse(fg, &col);
tag = gtk_text_tag_new(name);
g_object_set(G_OBJECT(tag), "foreground-gdk", &col, NULL);
return tag;
}
void
gm_scripts_dialog_init() {
GmScripts *scripts = gm_app_scripts(gm_app_instance());
scripts_dialog = g_new0(GmScriptsDialog, 1);
scripts_dialog->tag_table = g_object_ref(gtk_text_tag_table_new());
scripts_dialog->dialog = NULL;
gtk_text_tag_table_add(scripts_dialog->tag_table,
gm_scripts_dialog_create_tag("scripts-run", "#445632"));
gtk_text_tag_table_add(scripts_dialog->tag_table,
gm_scripts_dialog_create_tag("scripts-msg", "#314E6C"));
gtk_text_tag_table_add(scripts_dialog->tag_table,
gm_scripts_dialog_create_tag("scripts-error", "#663822"));
scripts_dialog->text_buffer_console =
g_object_ref(gtk_text_buffer_new(scripts_dialog->tag_table));
// Attach signals to GmScripts object to know about changes
g_signal_connect(scripts, "script_added",
G_CALLBACK(on_gm_scripts_dialog_script_added), NULL);
g_signal_connect(scripts, "script_changed",
G_CALLBACK(on_gm_scripts_dialog_script_changed), NULL);
g_signal_connect(scripts, "script_removed",
G_CALLBACK(on_gm_scripts_dialog_script_removed), NULL);
g_signal_connect(scripts, "message",
G_CALLBACK(on_gm_scripts_dialog_message), NULL);
g_signal_connect(scripts, "error",
G_CALLBACK(on_gm_scripts_dialog_error), NULL);
g_signal_connect(scripts, "run",
G_CALLBACK(on_gm_scripts_dialog_run), NULL);
}
void
gm_scripts_dialog_fini() {
g_object_unref(scripts_dialog->tag_table);
g_object_unref(scripts_dialog->text_buffer_console);
g_free(scripts_dialog);
}
gboolean
gm_scripts_scroll_end_idle(gpointer user_data) {
//gm_text_scroller_scroll_end(scripts_dialog->text_scroller);
return FALSE;
}
void
gm_scripts_dialog_run(GmAppView *view) {
GtkWidget *editor;
if (scripts_dialog->dialog != NULL) {
gtk_window_present(GTK_WINDOW(scripts_dialog->dialog));
return;
}
scripts_dialog->xml = glade_xml_new(G_SCRIPTS_XML, "gm_scripts_dialog", NULL);
scripts_dialog->dialog = glade_xml_get_widget(scripts_dialog->xml,
"gm_scripts_dialog");
scripts_dialog->text_view_console = glade_xml_get_widget(scripts_dialog->xml,
"text_view_console");
scripts_dialog->statusbar_editor = glade_xml_get_widget(scripts_dialog->xml,
"statusbar_editor");
scripts_dialog->button_save = glade_xml_get_widget(scripts_dialog->xml,
"tool_button_save");
scripts_dialog->tree_view_scripts = glade_xml_get_widget(scripts_dialog->xml,
"tree_view_scripts");
scripts_dialog->tree_view_files = glade_xml_get_widget(scripts_dialog->xml,
"tree_view_files");
scripts_dialog->button_delete = glade_xml_get_widget(scripts_dialog->xml,
"tool_button_delete");
// Create new text scroller, this object will take care of itself and will
// destroy itself when the view dies, neat!
scripts_dialog->text_scroller = gm_text_scroller_new(GTK_TEXT_VIEW(
scripts_dialog->text_view_console));
gm_scripts_dialog_init_console();
gm_scripts_dialog_init_tree();
gm_scripts_dialog_init_files();
editor = gm_scripts_dialog_init_editor();
gtk_container_add(GTK_CONTAINER(glade_xml_get_widget(scripts_dialog->xml,
"scrolled_window_editor")), editor);
glade_xml_signal_connect(scripts_dialog->xml, "on_gm_scripts_dialog_delete",
G_CALLBACK(on_gm_scripts_dialog_delete));
glade_xml_signal_connect(scripts_dialog->xml,
"on_tree_view_files_row_activated",
G_CALLBACK(on_tree_view_files_row_activated));
glade_xml_signal_connect(scripts_dialog->xml,
"on_tool_button_new_clicked",
G_CALLBACK(on_tool_button_new_clicked));
glade_xml_signal_connect(scripts_dialog->xml,
"on_tool_button_save_clicked",
G_CALLBACK(on_tool_button_save_clicked));
glade_xml_signal_connect(scripts_dialog->xml,
"on_tool_button_save_as_clicked",
G_CALLBACK(on_tool_button_save_as_clicked));
glade_xml_signal_connect(scripts_dialog->xml,
"on_tool_button_delete_clicked",
G_CALLBACK(on_tool_button_delete_clicked));
gtk_window_set_transient_for(GTK_WINDOW(scripts_dialog->dialog),
GTK_WINDOW(view));
gm_scripts_dialog_set_status(_("New <untitled>"));
g_object_unref(scripts_dialog->xml);
g_idle_add((GSourceFunc)(gm_scripts_scroll_end_idle), NULL);
}
void
gm_scripts_dialog_add(MessageType mtype, gchar *msg) {
gchar *m, *tagName = NULL, *newLine;
gchar p[3] = {' ', ' ', '\0'};
GtkTextIter end;
switch (mtype) {
case SCRIPT_RUN:
p[0] = ':';
tagName = g_strdup("scripts-run");
break;
case SCRIPT_MESSAGE:
p[0] = '#';
tagName = g_strdup("scripts-msg");
break;
case SCRIPT_ERROR:
p[0] = '!';
tagName = g_strdup("scripts-error");
break;
}
m = g_strconcat(p, msg, "\n", NULL);
gtk_text_buffer_get_end_iter(scripts_dialog->text_buffer_console, &end);
// convert to UTF-8
newLine = g_locale_to_utf8(m, strlen(m), NULL, NULL, NULL);
if (newLine == NULL) {
gtk_text_buffer_insert_with_tags_by_name(
scripts_dialog->text_buffer_console, &end, m, strlen(m),
tagName, NULL);
} else {
gtk_text_buffer_insert_with_tags_by_name(
scripts_dialog->text_buffer_console, &end, newLine,
strlen(newLine), tagName, NULL);
g_free(newLine);
}
g_free(m);
g_free(tagName);
}
gboolean
gm_scripts_dialog_selection_info(SelectionInfo *info) {
GtkTreeView *view = GTK_TREE_VIEW(scripts_dialog->tree_view_files);
GtkTreeModel *model = gtk_tree_view_get_model(view);
GtkTreeSelection *selection = gtk_tree_view_get_selection(view);
gchar *parentName, *name, *filename;
info->view = view;
info->model = model;
if (gtk_tree_selection_get_selected(selection, &model, &(info->seliter))) {
info->has_parent = gtk_tree_model_iter_parent(model,
&(info->parent), &(info->seliter));
if (info->has_parent) {
gtk_tree_model_get(model, &(info->parent), SCRIPTS_NAME,
&parentName, -1);
gtk_tree_model_get(model, &(info->seliter), SCRIPTS_NAME,
&name, -1);
if (strcmp(parentName, _("Share")) == 0) {
filename = g_strconcat(
PACKAGE_DATA_DIR "/" PACKAGE "/scripts/", name, NULL);
} else {
filename = g_strconcat(gm_app_path(gm_app_instance()),
"/scripts/", name, NULL);
}
info->filename = g_filename_from_utf8(filename, -1,
NULL, NULL, NULL);
g_free(name);
g_free(parentName);
g_free(filename);
info->can_save = gm_scripts_dialog_can_write(info->filename);
} else {
info->filename = NULL;
}
return TRUE;
} else {
return FALSE;
}
}
/* CALLBACKS */
gboolean
on_gm_scripts_dialog_delete(GtkWidget *widget, GdkEvent *event,
gpointer user_data) {
g_free(scripts_dialog->current_edit);
scripts_dialog->current_edit = NULL;
scripts_dialog->dialog = NULL;
return FALSE;
}
void
on_files_changed(GtkTreeSelection *treeselection, gpointer user_data) {
SelectionInfo info;
if (gm_scripts_dialog_selection_info(&info)) {
gtk_widget_set_sensitive(scripts_dialog->button_delete,
info.has_parent && info.can_save);
if (info.filename) {
g_free(info.filename);
}
}
}
void
on_tree_view_files_row_activated(GtkTreeView *treeview, GtkTreePath *arg1,
GtkTreeViewColumn *arg2, gpointer user_data) {
SelectionInfo si;
if (gm_scripts_dialog_selection_info(&si) && si.filename) {
gm_scripts_dialog_editor_load(&si);
g_free(si.filename);
}
}
void
on_text_buffer_editor_modified_changed(GtkTextBuffer *buffer,
gpointer user_data) {
gchar *msg;
gboolean modified = gtk_text_buffer_get_modified(buffer);
if (modified) {
if (scripts_dialog->current_edit) {
msg = g_strconcat(_("Changed "), scripts_dialog->current_edit, NULL);
} else {
msg = g_strdup(_("Changed <untitled>"));
}
gm_scripts_dialog_set_status(msg);
g_free(msg);
}
}
void
on_tool_button_new_clicked(GtkToolButton *button, gpointer user_data) {
gm_scripts_dialog_editor_load(NULL);
}
void
on_tool_button_save_clicked(GtkToolButton *button, gpointer user_data) {
if (scripts_dialog->current_edit) {
gm_scripts_dialog_editor_save(scripts_dialog->current_edit);
} else {
on_tool_button_save_as_clicked(button, user_data);
}
}
void
on_tool_button_save_as_clicked(GtkToolButton *button, gpointer user_data) {
GtkWidget *dlg;
gchar *filename, *di;
dlg = gtk_file_chooser_dialog_new(_("Save file"),
GTK_WINDOW(scripts_dialog->dialog),
GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
NULL);
if (scripts_dialog->current_edit) {
gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dlg),
scripts_dialog->current_edit);
} else {
di = g_strconcat(gm_app_path(gm_app_instance()),
"/scripts/untitled.rb", NULL);
gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(dlg), di);
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dlg), "untitled.rb");
g_free(di);
}
if (gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_ACCEPT) {
filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg));
gm_scripts_dialog_editor_save(filename);
g_free(filename);
}
gtk_widget_destroy(dlg);
}
void
on_tool_button_delete_clicked(GtkToolButton *button, gpointer user_data) {
SelectionInfo info;
if (gm_scripts_dialog_selection_info(&info)) {
if (info.has_parent && info.can_save) {
remove(info.filename);
}
g_free(info.filename);
}
}
void
on_gm_scripts_dialog_script_added(GmScripts *scripts, GmScript *script,
gpointer user_data) {
if (scripts_dialog->dialog != NULL) {
gm_scripts_dialog_tree_add_script(script);
gm_scripts_dialog_add_file(script);
}
}
void
on_gm_scripts_dialog_script_changed(GmScripts *scripts, GmScript *script,
gpointer user_data) {
if (scripts_dialog->dialog != NULL) {
gm_scripts_dialog_tree_update_script(script);
}
}
void
on_gm_scripts_dialog_script_removed(GmScripts *scripts, GmScript *script,
gpointer user_data) {
if (scripts_dialog->dialog != NULL) {
gm_scripts_dialog_tree_remove_script(script);
gm_scripts_dialog_remove_file(script);
if (strcmp(script->filename, scripts_dialog->current_edit) == 0) {
gm_scripts_dialog_editor_load(NULL);
}
}
}
void on_gm_scripts_dialog_message(GmScripts *scripts, gchar *message,
gpointer user_data) {
gm_scripts_dialog_add(SCRIPT_MESSAGE, message);
}
void on_gm_scripts_dialog_error(GmScripts *scripts, gchar *message,
gpointer user_data) {
gm_scripts_dialog_add(SCRIPT_ERROR, message);
}
void on_gm_scripts_dialog_run(GmScripts *scripts, gchar *message,
gpointer user_data) {
gm_scripts_dialog_add(SCRIPT_RUN, message);
}
#endif

View File

@ -0,0 +1,18 @@
#ifndef __GM_SCRIPTS_DIALOG_H__
#define __GM_SCRIPTS_DIALOG_H__
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_RUBY
#include <gtk/gtk.h>
#include "../gm-world.h"
#include "../widgets/gm-app-view.h"
void gm_scripts_dialog_run(GmAppView *view);
void gm_scripts_dialog_init();
void gm_scripts_dialog_fini();
#endif
#endif /* __GM_SCRIPTS_DIALOG_H__ */

View File

@ -0,0 +1,991 @@
#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 <libgnomeui/libgnomeui.h>
#include <glade/glade.h>
#include "../gm-support.h"
#include "../gm-debug.h"
#include "gm-triggers-dialog.h"
#include "../gm-app.h"
#include "../gm-pixbuf.h"
#include "../gm-scripts.h"
typedef struct _GmTriggersDialog {
GtkWidget *dialog;
GladeXML *xml;
GmWorld *world;
GmTrigger *trigger;
gboolean is_new;
GtkWidget *entry_name;
GtkWidget *button_ok;
GtkWidget *button_next;
GtkWidget *vbox_conditions;
GtkWidget *vbox_actions;
GtkWidget *notebook_triggers;
GtkWidget *tree_view_event_types;
GtkWidget *hbox_add_condition;
GtkWidget *hbox_add_action;
GtkTreeModel *action_model;
GtkTreeModel *condition_model;
GtkTreeModel *highlight_model;
#ifdef HAVE_RUBY
GtkTreeModel *script_model;
#endif
} GmTriggersDialog;
enum {
NO_ARGS,
SINGLE_ARGS,
CUSTOM_ARGS
};
typedef enum _CustomArgType {
CUSTOM_ARG_CREATE,
CUSTOM_ARG_GET_DATA
} CustomArgType;
gpointer gm_triggers_dialog_custom_arg_highlight(GmTriggersDialog *triggers,
CustomArgType type, gpointer data, gpointer user_data);
gpointer gm_triggers_dialog_custom_arg_browse(GmTriggersDialog *triggers,
CustomArgType type, gpointer data, gpointer user_data);
#ifdef HAVE_RUBY
gpointer gm_triggers_dialog_custom_arg_script(GmTriggersDialog *triggers,
CustomArgType type, gpointer data, gpointer user_data);
#endif
#define CUSTOM_ARG_FUNC(x) (CustomArgFunc *)(x)
typedef gpointer (*CustomArgFunc) (GmTriggersDialog *triggers,
CustomArgType type, gpointer data, gpointer user_data);
enum {
COLUMN_NAME,
COLUMN_DATA,
N_COLUMNS
};
enum {
COLUMN_HIGH_NAME,
COLUMN_HIGH_TAG,
N_COLUMNS_HIGH
};
typedef struct _ModelData {
gint type;
gchar *title;
gint args;
CustomArgFunc func;
} ModelData;
typedef struct _TagPair {
const gchar *name;
const gchar *tag;
} TagPair;
typedef struct _ComboBoxTypeData {
GmTriggersDialog *triggers;
GmTriggerData *data;
} ComboBoxTypeData;
static const TagPair highlightTags[] = {
{N_("Black"), "bg_black"},
{N_("Red"), "bg_red"},
{N_("Green"), "bg_green"},
{N_("Yellow"), "bg_yellow"},
{N_("Blue"), "bg_blue"},
{N_("Purple"), "bg_purple"},
{N_("Cyan"), "bg_cyan"},
{N_("White"), "bg_white"},
{NULL, NULL}
};
static const ModelData dataConditionOutput[] = {
{TCT_CONTAINS, N_("Contains"), SINGLE_ARGS, NULL},
{TCT_NOT_CONTAINS, N_("Not contains"), SINGLE_ARGS, NULL},
{TCT_BEGINS, N_("Begins with"), SINGLE_ARGS, NULL},
{TCT_NOT_BEGINS, N_("Not begins with"), SINGLE_ARGS, NULL},
{TCT_ENDS, N_("Ends with"), SINGLE_ARGS, NULL},
{TCT_NOT_ENDS, N_("Not ends with"), SINGLE_ARGS, NULL},
{TCT_MATCHES, N_("Matches"), SINGLE_ARGS, NULL},
{TCT_NOT_MATCHES, N_("Not matches"), SINGLE_ARGS, NULL},
{-1, NULL, 0, NULL}
};
static const ModelData dataConditionUsers[] = {
{TCT_USER_ONLINE, N_("Online"), SINGLE_ARGS, NULL},
{TCT_USER_OFFLINE, N_("Offline"), SINGLE_ARGS, NULL},
{TCT_USER_IDLE, N_("Idle"), SINGLE_ARGS, NULL},
{TCT_USER_IDLE_OFF, N_("No longer idle"), SINGLE_ARGS, NULL},
{TCT_USER_AWAY, N_("Away"), SINGLE_ARGS, NULL},
{TCT_USER_AWAY_OFF, N_("No longer away"), SINGLE_ARGS, NULL},
{-1, NULL, 0, NULL}
};
static const ModelData dataActionOutput[] = {
{TAT_HIGHLIGHT_LINE, N_("Highlight line"), CUSTOM_ARGS,
gm_triggers_dialog_custom_arg_highlight},
{TAT_HIGHLIGHT_MATCH, N_("Highlight match"), CUSTOM_ARGS,
gm_triggers_dialog_custom_arg_highlight},
{TAT_BEEP, N_("Beep"), NO_ARGS, NULL},
{TAT_PLAY_SOUND, N_("Play sound"), CUSTOM_ARGS,
gm_triggers_dialog_custom_arg_browse},
{TAT_NOTIFY, N_("Notify"), SINGLE_ARGS, NULL},
#ifdef HAVE_RUBY
{TAT_RUN_SCRIPT, N_("Run script"), CUSTOM_ARGS,
gm_triggers_dialog_custom_arg_script},
#endif
{TAT_RUN, N_("Run"), CUSTOM_ARGS,
gm_triggers_dialog_custom_arg_browse},
{-1, NULL, 0, NULL}
};
static const ModelData dataActionUsers[] = {
{TAT_BEEP, N_("Beep"), NO_ARGS, NULL},
{TAT_PLAY_SOUND, N_("Play sound"), CUSTOM_ARGS,
gm_triggers_dialog_custom_arg_browse},
{TAT_NOTIFY, N_("Notify"), SINGLE_ARGS, NULL},
#ifdef HAVE_RUBY
{TAT_RUN_SCRIPT, N_("Run script"), CUSTOM_ARGS,
gm_triggers_dialog_custom_arg_script},
#endif
{TAT_RUN, N_("Run"), CUSTOM_ARGS,
gm_triggers_dialog_custom_arg_browse},
{-1, NULL, 0, NULL}
};
void on_button_next_clicked(GtkButton *button, GmTriggersDialog *triggers);
void on_button_add_condition_clicked(GtkButton *button,
GmTriggersDialog *triggers);
void on_button_add_action_clicked(GtkButton *button,
GmTriggersDialog *triggers);
void on_notebook_triggers_switch_page(GtkNotebook *notebook, GtkNotebookPage
*page, guint page_num, GmTriggersDialog *triggers);
void on_combo_box_type_changed(GtkComboBox *widget, ComboBoxTypeData *tdata);
void on_combo_box_type_destroy(GtkObject *object, ComboBoxTypeData *tdata);
void on_button_remove_clicked(GtkButton *button, GmTriggersDialog *triggers);
void on_tree_view_event_types_changed(GtkTreeSelection *treeselection,
GmTriggersDialog *triggers);
#define G_TRIGGERS_XML PACKAGE_DATA_DIR "/" PACKAGE "/ui/gm-triggers.glade"
void
gm_triggers_dialog_create_models(GmTriggersDialog *triggers,
const ModelData *conditionData, const ModelData *actionData) {
int i;
GtkTreeIter iter;
ModelData *data;
GList *scripts, *item;
#ifdef HAVE_RUBY
GmScript *script;
GmScriptFunction *func;
#endif
triggers->action_model = GTK_TREE_MODEL(gtk_list_store_new(
N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER));
triggers->condition_model = GTK_TREE_MODEL(gtk_list_store_new(
N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER));
triggers->highlight_model = GTK_TREE_MODEL(gtk_list_store_new(
N_COLUMNS_HIGH, G_TYPE_STRING, G_TYPE_STRING));
#ifdef HAVE_RUBY
triggers->script_model = GTK_TREE_MODEL(gtk_list_store_new(1,
G_TYPE_STRING));
#endif
for (i = 0; conditionData[i].type != -1; i++) {
data = (ModelData *)(&(conditionData[i]));
gtk_list_store_append(GTK_LIST_STORE(triggers->condition_model), &iter);
gtk_list_store_set(GTK_LIST_STORE(triggers->condition_model), &iter,
COLUMN_NAME, _(data->title), COLUMN_DATA, data, -1);
}
for (i = 0; actionData[i].type != -1; i++) {
data = (ModelData *)(&(actionData[i]));
gtk_list_store_append(GTK_LIST_STORE(triggers->action_model), &iter);
gtk_list_store_set(GTK_LIST_STORE(triggers->action_model), &iter,
COLUMN_NAME, _(data->title), COLUMN_DATA, data, -1);
}
for (i = 0; highlightTags[i].name != NULL; i++) {
gtk_list_store_append(GTK_LIST_STORE(triggers->highlight_model), &iter);
gtk_list_store_set(GTK_LIST_STORE(triggers->highlight_model), &iter,
COLUMN_HIGH_NAME, _(highlightTags[i].name), COLUMN_HIGH_TAG,
highlightTags[i].tag, -1);
}
#ifdef HAVE_RUBY
for (scripts = gm_scripts_scripts(gm_app_scripts(gm_app_instance()));
scripts; scripts = scripts->next) {
script = (GmScript *)(scripts->data);
for (item = script->functions; item; item = item->next) {
func = (GmScriptFunction *)(item->data);
gtk_list_store_append(GTK_LIST_STORE(triggers->script_model),
&iter);
gtk_list_store_set(GTK_LIST_STORE(triggers->script_model), &iter,
0, func->name, -1);
}
}
#endif
}
ModelData *
gm_triggers_dialog_combo_get_selected_data(GmTriggersDialog *triggers,
GtkComboBox *combo) {
GtkTreeIter iter;
ModelData *data;
gtk_combo_box_get_active_iter(combo, &iter);
gtk_tree_model_get(gtk_combo_box_get_model(combo), &iter, COLUMN_DATA, &data,
-1);
return data;
}
GList *
gm_triggers_dialog_collect_rules(GmTriggersDialog *triggers, GtkWidget *vbox) {
gchar *text;
GList *children, *child, *item, *result = NULL;
GtkComboBox *combo;
GtkEntry *entry;
ModelData *data;
GmTriggerData *d;
children = gtk_container_get_children(GTK_CONTAINER(vbox));
for (item = children; item; item = item->next) {
if (item->data == triggers->hbox_add_condition ||
item->data == triggers->hbox_add_action) {
break;
}
if (GTK_IS_HBOX(item->data)) {
child = gtk_container_get_children(GTK_CONTAINER(item->data));
combo = GTK_COMBO_BOX(child->data);
data = gm_triggers_dialog_combo_get_selected_data(triggers, combo);
d = NULL;
switch (data->args) {
case NO_ARGS:
d = gm_trigger_data_new(data->type, NULL);
break;
case SINGLE_ARGS:
if (child->next) {
entry = GTK_ENTRY(child->next->data);
text = (gchar *)gtk_entry_get_text(entry);
if (g_utf8_strlen(text, -1) > 0) {
d = gm_trigger_data_new(data->type, g_strdup(text));
}
}
break;
case CUSTOM_ARGS:
text = (gchar *)(data->func(triggers, CUSTOM_ARG_GET_DATA,
child->next, NULL));
if (text != NULL && g_utf8_strlen(text, -1) > 0) {
d = gm_trigger_data_new(data->type, text);
} else {
g_free(text);
}
break;
default:
break;
}
if (d) {
result = g_list_append(result, d);
}
g_list_free(child);
}
}
g_list_free(children);
return result;
}
gboolean
gm_triggers_dialog_fill_trigger(GmTriggersDialog *triggers) {
const gchar *name = gtk_entry_get_text(GTK_ENTRY(triggers->entry_name));
GList *conditions = NULL;
GList *actions = NULL;
if (g_utf8_strlen(name, -1) == 0) {
gm_error_dialog(_("Please fill in a trigger name"),
GTK_WINDOW(triggers->dialog));
gtk_widget_grab_focus(triggers->entry_name);
return FALSE;
}
conditions = gm_triggers_dialog_collect_rules(triggers,
triggers->vbox_conditions);
if (conditions == NULL) {
gm_error_dialog(_("Please specify at least one condition"),
GTK_WINDOW(triggers->dialog));
return FALSE;
}
actions = gm_triggers_dialog_collect_rules(triggers, triggers->vbox_actions);
if (actions == NULL) {
gm_trigger_free_list(conditions);
gm_error_dialog(_("Please specify at least one action"),
GTK_WINDOW(triggers->dialog));
return FALSE;
}
gm_trigger_set_name(triggers->trigger, name);
gm_trigger_set_conditions(triggers->trigger, conditions);
gm_trigger_set_actions(triggers->trigger, actions);
return TRUE;
}
void
gm_triggers_dialog_initialize_event_types(GmTriggersDialog *triggers) {
GtkListStore *store = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING,
G_TYPE_INT);
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkTreeIter iter;
gtk_tree_view_set_model(GTK_TREE_VIEW(triggers->tree_view_event_types),
GTK_TREE_MODEL(store));
renderer = gtk_cell_renderer_pixbuf_new();
column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "pixbuf",
0, NULL);
gtk_tree_view_column_set_min_width(column, 40);
gtk_tree_view_append_column(GTK_TREE_VIEW(triggers->tree_view_event_types),
column);
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "markup",
1, NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(triggers->tree_view_event_types),
column);
gtk_list_store_prepend(store, &iter);
gtk_list_store_set(store, &iter, 0,
gm_pixbuf_get_at_size("ice-userlist/programmer.svg", 32, 32), 1,
_("<b>Player event</b>\nPlayer events are triggered on userlist "
"activity"), 2, TT_USERS, -1);
gtk_list_store_prepend(store, &iter);
gtk_list_store_set(store, &iter, 0,
gm_pixbuf_get_at_size("world.svg", 32, 32), 1,
_("<b>World event</b>\nWorld events are triggered on incoming "
"lines of text"), 2, TT_OUTPUT, -1);
gtk_tree_selection_select_iter(gtk_tree_view_get_selection(
GTK_TREE_VIEW(triggers->tree_view_event_types)), &iter);
g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(
triggers->tree_view_event_types)), "changed",
G_CALLBACK(on_tree_view_event_types_changed), triggers);
}
GmTrigger *
gm_triggers_dialog_run_dialog(GmTriggersDialog *triggers) {
gboolean done = FALSE;
GmTrigger *result = NULL;
while (!done) {
done = TRUE;
switch (gtk_dialog_run(GTK_DIALOG(triggers->dialog))) {
case GTK_RESPONSE_OK:
done = gm_triggers_dialog_fill_trigger(triggers);
if (done) {
result = triggers->trigger;
}
break;
default:
break;
}
}
if (triggers->action_model != NULL)
g_object_unref(triggers->action_model);
if (triggers->condition_model != NULL)
g_object_unref(triggers->condition_model);
if (triggers->highlight_model != NULL)
g_object_unref(triggers->highlight_model);
#ifdef HAVE_RUBY
if (triggers->script_model != NULL)
g_object_unref(triggers->script_model);
#endif
if (!result && triggers->is_new) {
gm_trigger_free(triggers->trigger);
}
g_object_unref(triggers->xml);
gtk_widget_destroy(triggers->dialog);
g_free(triggers);
return result;
}
GmTrigger *
gm_triggers_dialog_run_priv(GmWorld *world, GmTrigger *trigger,
gboolean is_new) {
GmTriggersDialog *triggers = g_new0(GmTriggersDialog, 1);
triggers->is_new = is_new;
triggers->world = world;
triggers->xml = glade_xml_new(G_TRIGGERS_XML, "gm_triggers_dialog", NULL);
triggers->dialog = glade_xml_get_widget(triggers->xml,
"gm_triggers_dialog");
triggers->entry_name = glade_xml_get_widget(triggers->xml, "entry_name");
triggers->vbox_conditions = glade_xml_get_widget(triggers->xml,
"vbox_conditions");
triggers->vbox_actions = glade_xml_get_widget(triggers->xml,
"vbox_actions");
triggers->notebook_triggers = glade_xml_get_widget(triggers->xml,
"notebook_triggers");
triggers->tree_view_event_types = glade_xml_get_widget(triggers->xml,
"tree_view_event_types");
triggers->button_ok = glade_xml_get_widget(triggers->xml, "button_ok");
triggers->hbox_add_condition = glade_xml_get_widget(triggers->xml,
"hbox_add_condition");
triggers->hbox_add_action = glade_xml_get_widget(triggers->xml,
"hbox_add_action");
triggers->button_next = glade_xml_get_widget(triggers->xml, "button_next");
gm_triggers_dialog_initialize_event_types(triggers);
glade_xml_signal_connect_data(triggers->xml, "on_button_next_clicked",
G_CALLBACK(on_button_next_clicked), triggers);
glade_xml_signal_connect_data(triggers->xml,
"on_button_add_condition_clicked",
G_CALLBACK(on_button_add_condition_clicked), triggers);
glade_xml_signal_connect_data(triggers->xml, "on_button_add_action_clicked",
G_CALLBACK(on_button_add_action_clicked), triggers);
glade_xml_signal_connect_data(triggers->xml,
"on_notebook_triggers_switch_page",
G_CALLBACK(on_notebook_triggers_switch_page), triggers);
triggers->trigger = trigger;
if (is_new) {
gtk_notebook_set_current_page(
GTK_NOTEBOOK(triggers->notebook_triggers), 0);
} else {
gtk_notebook_set_current_page(
GTK_NOTEBOOK(triggers->notebook_triggers), 1);
}
gtk_widget_show_all(triggers->dialog);
return gm_triggers_dialog_run_dialog(triggers);
}
GmTrigger *
gm_triggers_dialog_run(GmWorld *world, GmTrigger *trigger) {
return gm_triggers_dialog_run_priv(world, trigger, FALSE);
}
GmTrigger *
gm_triggers_dialog_run_new(GmWorld *world, GmTrigger *trigger) {
if (!trigger) {
trigger = gm_trigger_new();
}
return gm_triggers_dialog_run_priv(world, trigger, TRUE);
}
void
gm_triggers_dialog_select_combo_by_type(GmTriggersDialog *triggers,
GtkComboBox *combo, gint type) {
GtkTreeModel *model = gtk_combo_box_get_model(combo);
GtkTreeIter iter;
ModelData *data;
if (gtk_tree_model_get_iter_first(model, &iter)) {
do {
gtk_tree_model_get(model, &iter, COLUMN_DATA, &data, -1);
if (type == data->type) {
gtk_combo_box_set_active_iter(combo, &iter);
return;
}
} while (gtk_tree_model_iter_next(model, &iter));
}
}
GtkWidget *
gm_triggers_dialog_create_item(GmTriggersDialog *triggers, GtkTreeModel *model,
GmTriggerData *t) {
GtkWidget *hbox, *combo, *button;
ComboBoxTypeData *data;
GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
hbox = gtk_hbox_new(FALSE, 6);
combo = gtk_combo_box_new_with_model(model);
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer, "text",
COLUMN_NAME, NULL);
data = g_new0(ComboBoxTypeData, 1);
data->triggers = triggers;
data->data = t;
g_signal_connect(combo, "changed", G_CALLBACK(on_combo_box_type_changed),
data);
g_signal_connect(combo, "destroy", G_CALLBACK(on_combo_box_type_destroy),
data);
button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
g_signal_connect(button, "clicked", G_CALLBACK(on_button_remove_clicked),
triggers);
gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, TRUE, 0);
gtk_widget_show_all(hbox);
return hbox;
}
void
gm_triggers_dialog_new_condition(GmTriggersDialog *triggers) {
GtkWidget *hbox;
GList *children;
hbox = gm_triggers_dialog_create_item(triggers, triggers->condition_model,
NULL);
children = gtk_container_get_children(GTK_CONTAINER(hbox));
if (triggers->trigger->event == TT_OUTPUT) {
gm_triggers_dialog_select_combo_by_type(triggers,
GTK_COMBO_BOX(children->data), dataConditionOutput[0].type);
} else {
gm_triggers_dialog_select_combo_by_type(triggers,
GTK_COMBO_BOX(children->data), dataConditionUsers[0].type);
}
gtk_box_pack_start(GTK_BOX(triggers->vbox_conditions), hbox, FALSE, TRUE,
0);
g_list_free(children);
}
void
gm_triggers_dialog_populate_conditions(GmTriggersDialog *triggers) {
GList *item;
GtkWidget *hbox;
GmTriggerData *t;
GList *children;
if (triggers->trigger->conditions) {
for (item = triggers->trigger->conditions; item; item = item->next) {
t = (GmTriggerData *)(item->data);
hbox = gm_triggers_dialog_create_item(triggers,
triggers->condition_model, t);
children = gtk_container_get_children(GTK_CONTAINER(hbox));
gtk_box_pack_start(GTK_BOX(triggers->vbox_conditions), hbox,
FALSE, TRUE, 0);
gm_triggers_dialog_select_combo_by_type(triggers,
GTK_COMBO_BOX(children->data), t->type);
g_list_free(children);
}
} else {
gm_triggers_dialog_new_condition(triggers);
}
}
void
gm_triggers_dialog_new_action(GmTriggersDialog *triggers) {
GtkWidget *hbox;
GList *children;
hbox = gm_triggers_dialog_create_item(triggers, triggers->action_model,
NULL);
children = gtk_container_get_children(GTK_CONTAINER(hbox));
if (triggers->trigger->event == TT_OUTPUT) {
gm_triggers_dialog_select_combo_by_type(triggers,
GTK_COMBO_BOX(children->data), dataActionOutput[0].type);
} else {
gm_triggers_dialog_select_combo_by_type(triggers,
GTK_COMBO_BOX(children->data), dataActionUsers[0].type);
}
gtk_box_pack_start(GTK_BOX(triggers->vbox_actions), hbox, FALSE, TRUE, 0);
g_list_free(children);
}
void
gm_triggers_dialog_populate_actions(GmTriggersDialog *triggers) {
GList *item;
GtkWidget *hbox;
GmTriggerData *t;
GList *children;
if (triggers->trigger->actions) {
for (item = triggers->trigger->actions; item; item = item->next) {
t = (GmTriggerData *)(item->data);
hbox = gm_triggers_dialog_create_item(triggers,
triggers->action_model, t);
children = gtk_container_get_children(GTK_CONTAINER(hbox));
gtk_box_pack_start(GTK_BOX(triggers->vbox_actions), hbox, FALSE,
TRUE, 0);
gm_triggers_dialog_select_combo_by_type(triggers,
GTK_COMBO_BOX(children->data), t->type);
g_list_free(children);
}
} else {
gm_triggers_dialog_new_action(triggers);
}
}
/* CALLBACKS */
void
on_button_next_clicked(GtkButton *button, GmTriggersDialog *triggers) {
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
gint type;
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
triggers->tree_view_event_types));
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
triggers->tree_view_event_types));
if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
gtk_tree_model_get(model, &iter, 2, &type, -1);
triggers->trigger->event = type;
gtk_notebook_set_current_page(GTK_NOTEBOOK(
triggers->notebook_triggers), 1);
} else {
gm_error_dialog(_("Select a event type first"),
GTK_WINDOW(triggers->dialog));
}
}
void
on_tree_view_event_types_changed(GtkTreeSelection *treeselection,
GmTriggersDialog *triggers) {
GtkTreeIter iter;
GtkTreeModel *model;
gint type;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
triggers->tree_view_event_types));
if (!gtk_tree_selection_get_selected(treeselection, &model, &iter)) {
gtk_widget_set_sensitive(triggers->button_next, FALSE);
return;
}
gtk_widget_set_sensitive(triggers->button_next, TRUE);
gtk_tree_model_get(model, &iter, 2, &type, -1);
switch (type) {
case TT_OUTPUT:
gtk_window_set_icon(GTK_WINDOW(triggers->dialog),
gm_pixbuf_get_at_size("world.svg", 16, 16));
break;
case TT_USERS:
gtk_window_set_icon(GTK_WINDOW(triggers->dialog),
gm_pixbuf_get_at_size("ice-userlist/programmer.svg",
16, 16));
break;
default:
break;
}
}
void
on_notebook_triggers_switch_page(GtkNotebook *notebook, GtkNotebookPage *page,
guint page_num, GmTriggersDialog *triggers) {
const ModelData *conditionData = NULL;
const ModelData *actionData = NULL;
if (page_num != 1) {
return;
}
gtk_widget_set_sensitive(triggers->button_ok, TRUE);
switch (triggers->trigger->event) {
case TT_OUTPUT:
gtk_window_set_icon(GTK_WINDOW(triggers->dialog),
gm_pixbuf_get_at_size("world.svg", 16, 16));
conditionData = dataConditionOutput;
actionData = dataActionOutput;
break;
case TT_USERS:
gtk_window_set_icon(GTK_WINDOW(triggers->dialog),
gm_pixbuf_get_at_size("ice-userlist/programmer.svg",
16, 16));
conditionData = dataConditionUsers;
actionData = dataActionUsers;
break;
default:
break;
}
gm_triggers_dialog_create_models(triggers, conditionData, actionData);
if (triggers->trigger->name) {
gtk_entry_set_text(GTK_ENTRY(triggers->entry_name),
triggers->trigger->name);
}
gm_triggers_dialog_populate_conditions(triggers);
gm_triggers_dialog_populate_actions(triggers);
}
gpointer
gm_triggers_dialog_custom_arg_highlight(GmTriggersDialog *triggers,
CustomArgType type, gpointer data, gpointer user_data) {
GtkWidget *hbox;
GtkWidget *combo;
GtkCellRenderer *renderer;
GList *item;
GtkTreeIter iter;
GtkTreeModel *model;
gchar *tag;
GmTriggerData *t = (GmTriggerData *)(user_data);
switch (type) {
case CUSTOM_ARG_CREATE:
hbox = GTK_WIDGET(data);
combo = gtk_combo_box_new_with_model(triggers->highlight_model);
renderer = gtk_cell_renderer_text_new();
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer,
"text", COLUMN_HIGH_NAME, NULL);
if (!user_data) {
gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
} else {
model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
if (gtk_tree_model_get_iter_first(model, &iter)) {
do {
gtk_tree_model_get(model, &iter, COLUMN_HIGH_TAG,
&tag, -1);
if (strcmp(tag, t->data) == 0) {
gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo),
&iter);
break;
}
g_free(tag);
} while (gtk_tree_model_iter_next(model, &iter));
}
}
gtk_widget_show(combo);
gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
break;
case CUSTOM_ARG_GET_DATA:
item = (GList *)(data);
model = gtk_combo_box_get_model(GTK_COMBO_BOX(item->data));
gtk_combo_box_get_active_iter(GTK_COMBO_BOX(item->data), &iter);
gtk_tree_model_get(model, &iter, COLUMN_HIGH_TAG, &tag, -1);
return tag;
break;
}
return NULL;
}
void
on_button_browse_clicked(GtkButton *widget, GtkEntry *entry) {
gchar *tmp;
GtkWidget *d = gtk_file_chooser_dialog_new(_("Select file"),
NULL, GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
NULL);
if (gtk_dialog_run(GTK_DIALOG(d)) == GTK_RESPONSE_ACCEPT) {
tmp = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(d));
gtk_entry_set_text(entry, tmp);
g_free(tmp);
}
gtk_widget_destroy(d);
}
gpointer
gm_triggers_dialog_custom_arg_browse(GmTriggersDialog *triggers,
CustomArgType type, gpointer data, gpointer user_data) {
GtkWidget *hbox;
GtkWidget *entry;
GtkWidget *browse, *tmp;
GmTriggerData *t = (GmTriggerData *)(user_data);
GList *item;
switch (type) {
case CUSTOM_ARG_CREATE:
hbox = GTK_WIDGET(data);
entry = gtk_entry_new();
if (t) {
gtk_entry_set_text(GTK_ENTRY(entry), t->data);
}
browse = gtk_button_new();
tmp = gtk_hbox_new(FALSE, 3);
gtk_container_add(GTK_CONTAINER(browse), tmp);
gtk_box_pack_start(GTK_BOX(tmp), gtk_image_new_from_stock(
GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON), FALSE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(tmp), gtk_label_new(_("Browse")),
TRUE, TRUE, 0);
g_signal_connect(browse, "clicked",
G_CALLBACK(on_button_browse_clicked), entry);
gtk_widget_show(entry);
gtk_widget_show_all(browse);
gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(hbox), browse, FALSE, TRUE, 0);
break;
case CUSTOM_ARG_GET_DATA:
item = (GList *)(data);
return g_strdup(gtk_entry_get_text(GTK_ENTRY(item->data)));
break;
}
return NULL;
}
void
on_combo_box_type_changed(GtkComboBox *widget, ComboBoxTypeData *tdata) {
GtkWidget *parent = gtk_widget_get_parent(GTK_WIDGET(widget));
GList *item, *children = gtk_container_get_children(GTK_CONTAINER(parent));
ModelData *data;
GtkTreeIter iter;
GtkWidget *tmp;
for (item = children->next; item && item->next; item = item->next) {
gtk_widget_destroy(GTK_WIDGET(item->data));
}
g_list_free(children);
if (gtk_combo_box_get_active_iter(widget, &iter)) {
gtk_tree_model_get(gtk_combo_box_get_model(widget), &iter,
COLUMN_DATA, &data, -1);
switch (data->args) {
case SINGLE_ARGS:
tmp = gtk_entry_new();
if (tdata->data) {
gtk_entry_set_text(GTK_ENTRY(tmp), tdata->data->data);
}
gtk_widget_show(tmp);
gtk_box_pack_start(GTK_BOX(parent), tmp, TRUE, TRUE, 0);
break;
case CUSTOM_ARGS:
data->func(tdata->triggers,
CUSTOM_ARG_CREATE, (gpointer)parent,
(gpointer)tdata->data);
break;
default:
break;
}
if (tdata->data != NULL) {
tdata->data = NULL;
}
} else {
gm_debug_msg(DEBUG_ALWAYS, "No active iter!");
}
}
void
on_combo_box_type_destroy(GtkObject *object, ComboBoxTypeData *tdata) {
g_free(tdata);
}
#ifdef HAVE_RUBY
gpointer
gm_triggers_dialog_custom_arg_script(GmTriggersDialog *triggers,
CustomArgType type, gpointer data, gpointer user_data) {
GtkWidget *hbox;
GtkWidget *entry;
GtkEntryCompletion *entry_completion;
GList *item;
GmTriggerData *t = (GmTriggerData *)(user_data);
switch (type) {
case CUSTOM_ARG_CREATE:
hbox = GTK_WIDGET(data);
entry = gtk_entry_new();
entry_completion = gtk_entry_completion_new();
gtk_entry_completion_set_model(entry_completion,
triggers->script_model);
gtk_entry_completion_set_text_column(entry_completion, 0);
gtk_entry_set_completion(GTK_ENTRY(entry), entry_completion);
if (user_data) {
gtk_entry_set_text(GTK_ENTRY(entry), t->data);
}
gtk_widget_show(entry);
gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
break;
case CUSTOM_ARG_GET_DATA:
item = (GList *)(data);
return g_strdup(gtk_entry_get_text(GTK_ENTRY(item->data)));
break;
}
return NULL;
}
#endif
gboolean
gm_triggers_dialog_idle_remove_item(GtkWidget *parent) {
gtk_widget_destroy(parent);
return FALSE;
}
void
on_button_remove_clicked(GtkButton *button, GmTriggersDialog *triggers) {
g_idle_add((GSourceFunc)(gm_triggers_dialog_idle_remove_item),
gtk_widget_get_parent(GTK_WIDGET(button)));
}
void
on_button_add_condition_clicked(GtkButton *button, GmTriggersDialog *triggers) {
gm_triggers_dialog_new_condition(triggers);
}
void
on_button_add_action_clicked(GtkButton *button, GmTriggersDialog *triggers) {
gm_triggers_dialog_new_action(triggers);
}

View File

@ -0,0 +1,10 @@
#ifndef __GM_TRIGGERS_DIALOG__
#define __GM_TRIGGERS_DIALOG__
#include "../gm-triggers.h"
#include "../gm-world.h"
GmTrigger *gm_triggers_dialog_run(GmWorld *world, GmTrigger *trig);
GmTrigger *gm_triggers_dialog_run_new(GmWorld *world, GmTrigger *trig);
#endif /* __GM_TRIGGERS_DIALOG__ */

View File

@ -0,0 +1,111 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <gtk/gtk.h>
#include <glade/glade.h>
#include <libgnome/libgnome.h>
#include "../gm-world.h"
#include "../gm-support.h"
#include "../gm-pixbuf.h"
#define G_WORLD_INFO_XML PACKAGE_DATA_DIR "/" PACKAGE "/ui/gm-world-info.glade"
gboolean on_gm_world_info_dialog_url_button_release(GtkWidget *button,
GdkEventButton *event, gchar *link);
/* Private */
void
gm_world_info_dialog_set_label(GtkLabel *label, gchar *text) {
if (text) {
gtk_label_set_markup(label, text);
} else {
gtk_label_set_markup(label, _("<i>unspecified</i>"));
}
}
/* Public */
GtkDialog *
gm_world_info_dialog_new(GmWorldInfo world_info) {
GladeXML *xml;
GtkWidget *dlg, *widget;
gchar *tmp;
xml = glade_xml_new(G_WORLD_INFO_XML, "dlgWorldInfo", NULL);
dlg = glade_xml_get_widget(xml, "dlgWorldInfo");
gm_world_info_dialog_set_label(GTK_LABEL(glade_xml_get_widget(xml,
"lblName")), world_info.admin);
gm_world_info_dialog_set_label(GTK_LABEL(glade_xml_get_widget(xml,
"lblLocation")), world_info.location);
gm_world_info_dialog_set_label(GTK_LABEL(glade_xml_get_widget(xml,
"lblSystem")), world_info.system);
gm_world_info_dialog_set_label(GTK_LABEL(glade_xml_get_widget(xml,
"lblCharset")), world_info.charset);
gm_world_info_dialog_set_label(GTK_LABEL(glade_xml_get_widget(xml,
"lblLanguage")), world_info.language);
if (world_info.homepage) {
tmp = g_strconcat("<u><span color=\"#0000ff\">", world_info.homepage,
"</span></u>", NULL);
glade_xml_signal_connect_data(xml,
"on_gm_world_info_dialog_url_button_release",
G_CALLBACK(on_gm_world_info_dialog_url_button_release), NULL);
widget = glade_xml_get_widget(xml, "lblHomepage");
gtk_label_set_markup(GTK_LABEL(widget), tmp);
g_free(tmp);
}
if (world_info.contact) {
tmp = g_strconcat("<u><span color=\"#0000ff\">", world_info.contact,
"</span></u>", NULL);
glade_xml_signal_connect_data(xml,
"on_gm_world_info_dialog_url_button_release",
G_CALLBACK(on_gm_world_info_dialog_url_button_release), "mailto:");
widget = glade_xml_get_widget(xml, "lblEmail");
gtk_label_set_markup(GTK_LABEL(widget), tmp);
g_free(tmp);
}
if (world_info.logo) {
gtk_image_set_from_pixbuf(GTK_IMAGE(glade_xml_get_widget(xml,
"imageLogo")), gm_pixbuf_get(world_info.logo));
}
glade_xml_signal_connect(xml, "on_dlgWorldInfo_delete",
G_CALLBACK(gtk_widget_destroy));
glade_xml_signal_connect_data(xml, "on_buttonClose_clicked",
G_CALLBACK(gm_widget_destroy_data), dlg);
gtk_widget_show_all(dlg);
g_object_unref(xml);
return GTK_DIALOG(dlg);
}
/* Callbacks */
gboolean
on_gm_world_info_dialog_url_button_release(GtkWidget *label,
GdkEventButton *event, gchar *prefix) {
GError *err = NULL;
gchar *tmp, *link;
if (prefix != NULL) {
link = g_strconcat(prefix, gtk_label_get_text(GTK_LABEL(label)), NULL);
} else {
link = g_strdup(gtk_label_get_text(GTK_LABEL(label)));
}
if (!gnome_url_show(link, &err)) {
tmp = g_strdup_printf("Could not open link: %s", err->message);
gm_error_dialog(tmp, NULL);
g_free(tmp);
g_error_free(err);
}
g_free(link);
return FALSE;
}

View File

@ -0,0 +1,6 @@
#ifndef __GM_WORLD_INFO_DIALOG_H__
#define __GM_WORLD_INFO_DIALOG_H__
GtkDialog *gm_world_info_dialog_new(GmWorldInfo world_info);
#endif /* __GM_WORLD_INFO_DIALOG_H__ */

View File

@ -0,0 +1,116 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <gtk/gtk.h>
#include <glade/glade.h>
#include "../gm-world.h"
#include "../gm-support.h"
#define G_WORLD_LOGS_XML PACKAGE_DATA_DIR "/" PACKAGE "/ui/gm-world-info.glade"
void
on_gm_world_logs_dialog_row_activated(GtkTreeView *treeview, GtkTreePath *arg1,
GtkTreeViewColumn *arg2, GtkDialog *dlg);
/* Public */
GtkDialog *
gm_world_logs_dialog_new(GmWorld *world, GtkTreeView **view,
GtkProgressBar **progress) {
GladeXML *xml = glade_xml_new(G_WORLD_LOGS_XML, "dlgLogs", NULL);
GtkDialog *dlg;
GtkTreeModel *model, *smodel;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
gchar *path, *tmp, *tmp2;
const gchar *fname;
GDir *dir;
GError *err;
GtkTreeIter iter;
FILE *f;
long fsize;
int res = 0;
dlg = GTK_DIALOG(glade_xml_get_widget(xml, "dlgLogs"));
*view = GTK_TREE_VIEW(glade_xml_get_widget(xml, "tvwLogs"));
model = GTK_TREE_MODEL(gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING));
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes(_("Logs"), renderer,
"text", 1, NULL);
gtk_tree_view_append_column(*view, column);
smodel = gtk_tree_model_sort_new_with_model(model);
gtk_tree_view_column_set_sort_column_id(column, 0);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(smodel), 0,
GTK_SORT_DESCENDING);
gtk_tree_view_set_model(*view, GTK_TREE_MODEL(smodel));
/* Fill it in */
path = g_strdup_printf("%s/logs/", gm_world_path(world));
dir = g_dir_open(path, 0, &err);
if (dir) {
while ((fname = g_dir_read_name(dir)) != NULL) {
res++;
tmp = g_strconcat(path, fname, NULL);
f = fopen(tmp, "r");
if (f) {
fseek(f, 0, SEEK_END);
fsize = ftell(f);
fclose(f);
g_free(tmp);
tmp = gnome_vfs_format_file_size_for_display(fsize);
tmp2 = g_strconcat(fname, " (", tmp, ")", NULL);
gtk_list_store_prepend(GTK_LIST_STORE(model), &iter);
gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, fname, 1,
tmp2, -1);
g_free(tmp2);
}
g_free(tmp);
}
g_dir_close(dir);
if (res != 0) {
gtk_tree_model_get_iter_first(smodel, &iter);
gtk_tree_selection_select_iter(gtk_tree_view_get_selection(*view), &iter);
glade_xml_signal_connect_data(xml, "on_tvwLogs_row_activated",
G_CALLBACK(on_gm_world_logs_dialog_row_activated), dlg);
*progress = GTK_PROGRESS_BAR(glade_xml_get_widget(xml,
"progressBarLoading"));
} else {
gm_info_dialog(_("There are no log files for this world"), NULL);
gtk_widget_destroy(GTK_WIDGET(dlg));
dlg = NULL;
}
} else {
tmp = g_strdup_printf(_("Couldn't open the log directory: %s"), err->message);
gm_error_dialog(tmp, NULL);
g_error_free(err);
g_free(tmp);
gtk_widget_destroy(GTK_WIDGET(dlg));
dlg = NULL;
}
g_free(path);
g_object_unref(xml);
return dlg;
}
/* Callbacks */
void
on_gm_world_logs_dialog_row_activated(GtkTreeView *treeview, GtkTreePath *arg1,
GtkTreeViewColumn *arg2, GtkDialog *dlg) {
gtk_dialog_response(dlg, GTK_RESPONSE_OK);
}

View File

@ -0,0 +1,10 @@
#ifndef __GM_WORLD_LOGS_DIALOG_H__
#define __GM_WORLD_LOGS_DIALOG_H__
#include <gtk/gtk.h>
#include "../gm-world.h"
GtkDialog *gm_world_logs_dialog_new(GmWorld *world, GtkTreeView **view,
GtkProgressBar **progress);
#endif /* __GM_WORLD_LOGS_DIALOG_H__ */

View File

@ -0,0 +1,675 @@
#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 <stdlib.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include "gm-world-properties-dialog.h"
#include "gm-triggers-dialog.h"
#include "../gm-triggers.h"
#include "../gm-support.h"
#include "../gm-app.h"
#include "../gm-debug.h"
#include "../gm-options.h"
#include "../gm-pixbuf.h"
typedef struct _GmWorldPropertiesDialog {
GtkWidget *dialog;
GtkWidget *combo_box_charset;
GtkWidget *tree_view_triggers;
GladeXML *xml;
GmWorld *world;
gboolean is_new;
gulong handler_id;
} GmWorldPropertiesDialog;
static const encoding encodings[] = {
{"ISO-8859-1", N_("Western")},
{"ISO-8859-2", N_("Central European")},
{"ISO-8859-3", N_("South European")},
{"ISO-8859-4", N_("Baltic")},
{"ISO-8859-5", N_("Cyrillic")},
{"ISO-8859-6", N_("Arabic")},
{"ISO-8859-7", N_("Greek")},
{"ISO-8859-8", N_("Hebrew Visual")},
{"ISO-8859-8-I", N_("Hebrew")},
{"ISO-8859-9", N_("Turkish")},
{"ISO-8859-10", N_("Nordic")},
{"ISO-8859-13", N_("Baltic")},
{"ISO-8859-14", N_("Celtic")},
{"ISO-8859-15", N_("Western")},
{"ISO-8859-16", N_("Romanian")},
{"UTF-7", N_("Unicode")},
{"UTF-8", N_("Unicode")},
{"UTF-16", N_("Unicode")},
{"UCS-2", N_("Unicode")},
{"UCS-4", N_("Unicode")},
{"ARMSCII-8", N_("Armenian")},
{"BIG5", N_("Chinese Traditional")},
{"BIG5-HKSCS", N_("Chinese Traditional")},
{"CP866", N_("Cyrillic/Russian")},
{"EUC-JP", N_("Japanese")},
{"EUC-KR", N_("Korean")},
{"EUC-TW", N_("Chinese Traditional")},
{"GB18030", N_("Chinese Simplified")},
{"GB2312", N_("Chinese Simplified")},
{"GBK", N_("Chinese Simplified")},
{"GEORGIAN-ACADEMY", N_("Georgian")},
{"HZ", N_("Chinese Simplified")},
{"IBM850", N_("Western")},
{"IBM852", N_("Central European")},
{"IBM855", N_("Cyrillic")},
{"IBM857", N_("Turkish")},
{"IBM862", N_("Hebrew")},
{"IBM864", N_("Arabic")},
{"ISO-2022-JP", N_("Japanese")},
{"ISO-2022-KR", N_("Korean")},
{"ISO-IR-111", N_("Cyrillic")},
{"JOHAB", N_("Korean")},
{"KOI8R", N_("Cyrillic")},
{"KOI8-R", N_("Cyrillic")},
{"KOI8U", N_("Cyrillic/Ukrainian")},
{"SHIFT_JIS", N_("Japanese")},
{"TCVN", N_("Vietnamese")},
{"TIS-620", N_("Thai")},
{"UHC", N_("Korean")},
{"VISCII", N_("Vietnamese")},
{"WINDOWS-1250", N_("Central European")},
{"WINDOWS-1251", N_("Cyrillic")},
{"WINDOWS-1252", N_("Western")},
{"WINDOWS-1253", N_("Greek")},
{"WINDOWS-1254", N_("Turkish")},
{"WINDOWS-1255", N_("Hebrew")},
{"WINDOWS-1256", N_("Arabic")},
{"WINDOWS-1257", N_("Baltic")},
{"WINDOWS-1258", N_("Vietnamese")}
};
static GList *gm_world_properties_dialog_open = NULL;
void gm_world_properties_dialog_run_dialog(GmWorldPropertiesDialog *properties);
void on_button_add_trigger_clicked(GtkButton *button,
GmWorldPropertiesDialog *properties);
void on_button_edit_trigger_clicked(GtkButton *button,
GmWorldPropertiesDialog *properties);
void on_button_delete_trigger_clicked(GtkButton *button,
GmWorldPropertiesDialog *properties);
void on_tree_view_triggers_row_activated(GtkTreeView *treeview,
GtkTreePath *arg1, GtkTreeViewColumn *arg2,
GmWorldPropertiesDialog *properties);
void on_gm_world_properties_dialog_app_world_removed(GmApp *app, GmWorld *world,
GmWorldPropertiesDialog *properties);
void on_gm_world_properties_dialog_response(GtkDialog *dialog, gint response,
GmWorldPropertiesDialog *properties);
GtkWidget *
gm_world_properties_dialog_widget(GmWorldPropertiesDialog *properties,
gchar *name) {
return glade_xml_get_widget(properties->xml, name);
}
void
gm_world_properties_dialog_populate_charsets(
GmWorldPropertiesDialog *properties) {
int i;
const gchar *select = gm_options_get(gm_world_options(properties->world),
"charset");
gchar text[255];
GtkListStore *store;
GtkComboBoxEntry *combo = GTK_COMBO_BOX_ENTRY(properties->combo_box_charset);
store = gtk_list_store_new(1, G_TYPE_STRING);
gtk_combo_box_set_model(GTK_COMBO_BOX(combo), GTK_TREE_MODEL(store));
gtk_combo_box_entry_set_text_column(combo, 0);
for (i = 0; i < (int)(sizeof(encodings) / sizeof(encoding)); i++) {
sprintf(text, "%s - %s", encodings[i].charset, _(encodings[i].name));
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), text);
if (strcasecmp(encodings[i].charset, select) == 0) {
gtk_entry_set_text(GTK_ENTRY(GTK_BIN(combo)->child), text);
}
}
}
void
gm_world_properties_dialog_remove_trigger(GmWorldPropertiesDialog *properties,
GtkTreeIter *iter) {
GtkTreeView *tree_view = GTK_TREE_VIEW(properties->tree_view_triggers);
GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(tree_view));
gtk_list_store_remove(store, iter);
if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL) == 0) {
gtk_widget_set_sensitive(gm_world_properties_dialog_widget(properties,
"button_edit_trigger"), FALSE);
gtk_widget_set_sensitive(gm_world_properties_dialog_widget(properties,
"button_delete_trigger"), FALSE);
}
}
void
gm_world_properties_dialog_update_trigger(GmWorldPropertiesDialog *properties,
GtkTreeIter *iter, GmTrigger *t) {
GtkTreeView *tree_view = GTK_TREE_VIEW(properties->tree_view_triggers);
GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(tree_view));
gchar *text;
text = g_strdup_printf(_("<b>%s</b>\nConditions: %d\nActions: %d"),
t->name, g_list_length(t->conditions), g_list_length(t->actions));
gtk_list_store_set(store, iter, 1, text, 2, t, -1);
switch (t->event) {
case TT_OUTPUT:
gtk_list_store_set(store, iter, 0,
gm_pixbuf_get_at_size("world.svg", 32, 32), -1);
break;
case TT_USERS:
gtk_list_store_set(store, iter, 0,
gm_pixbuf_get_at_size("ice-userlist/programmer.svg", 32,
32), -1);
break;
default:
break;
}
g_free(text);
}
void
gm_world_properties_dialog_add_trigger(GmWorldPropertiesDialog *properties,
GmTrigger *t) {
GtkTreeView *tree_view = GTK_TREE_VIEW(properties->tree_view_triggers);
GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(tree_view));
GtkTreeIter iter;
gtk_list_store_append(store, &iter);
gm_world_properties_dialog_update_trigger(properties, &iter, t);
gtk_tree_selection_select_iter(gtk_tree_view_get_selection(tree_view),
&iter);
gtk_widget_set_sensitive(gm_world_properties_dialog_widget(properties,
"button_edit_trigger"), TRUE);
gtk_widget_set_sensitive(gm_world_properties_dialog_widget(properties,
"button_delete_trigger"), TRUE);
}
void
gm_world_properties_dialog_populate_tree_view_triggers(
GmWorldPropertiesDialog *properties) {
GtkTreeView *tree_view = GTK_TREE_VIEW(properties->tree_view_triggers);
GtkListStore *store = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING,
G_TYPE_POINTER);
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
const GList *item;
GmTrigger *t;
gtk_tree_view_set_model(GTK_TREE_VIEW(tree_view), GTK_TREE_MODEL(store));
renderer = gtk_cell_renderer_pixbuf_new();
column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "pixbuf",
0, NULL);
gtk_tree_view_column_set_min_width(column, 40);
gtk_tree_view_append_column(tree_view, column);
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "markup",
1, NULL);
gtk_tree_view_append_column(tree_view, column);
for (item = gm_triggers_list(gm_world_triggers(properties->world)); item;
item = item->next) {
t = gm_trigger_dup((GmTrigger *)(item->data));
gm_world_properties_dialog_add_trigger(properties, t);
}
if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL) == 0) {
gtk_widget_set_sensitive(gm_world_properties_dialog_widget(properties,
"button_edit_trigger"), FALSE);
gtk_widget_set_sensitive(gm_world_properties_dialog_widget(properties,
"button_delete_trigger"), FALSE);
}
}
GmWorldPropertiesDialog *
gm_world_properties_dialog_find(GmWorld *world) {
GmWorldPropertiesDialog *result = NULL;
GList *item;
for (item = gm_world_properties_dialog_open; item; item = item->next) {
result = (GmWorldPropertiesDialog *)(item->data);
if (result->world == world) {
return result;
}
}
return NULL;
}
void
gm_world_properties_dialog_initialize(GmWorldPropertiesDialog *properties) {
GmOptions *options = gm_world_options(properties->world);
const gchar *logo;
gtk_entry_set_text(GTK_ENTRY(gm_world_properties_dialog_widget(properties,
"entry_name")), gm_options_get(options, "name"));
gtk_entry_set_text(GTK_ENTRY(gm_world_properties_dialog_widget(properties,
"entry_host")), gm_options_get(options, "host"));
gtk_spin_button_set_value(GTK_SPIN_BUTTON(gm_world_properties_dialog_widget(
properties, "spin_button_port")), (double)(gm_options_get_int(
options, "port")));
gtk_entry_set_text(GTK_ENTRY(gm_world_properties_dialog_widget(properties,
"entry_player_name")), gm_options_get(options, "player_name"));
gtk_entry_set_text(GTK_ENTRY(gm_world_properties_dialog_widget(properties,
"entry_password")), gm_options_get(options, "password"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
gm_world_properties_dialog_widget(properties,
"check_button_auto_reconnect")),
gm_options_get_int(options, "reconnect"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
gm_world_properties_dialog_widget(properties,
"check_button_auto_load")),
gm_options_get_int(options, "autoload"));
gm_world_properties_dialog_populate_charsets(properties);
gm_world_properties_dialog_populate_tree_view_triggers(properties);
glade_xml_signal_connect_data(properties->xml,
"on_button_add_trigger_clicked",
G_CALLBACK(on_button_add_trigger_clicked), properties);
glade_xml_signal_connect_data(properties->xml,
"on_button_edit_trigger_clicked",
G_CALLBACK(on_button_edit_trigger_clicked), properties);
glade_xml_signal_connect_data(properties->xml,
"on_button_delete_trigger_clicked",
G_CALLBACK(on_button_delete_trigger_clicked), properties);
glade_xml_signal_connect_data(properties->xml,
"on_tree_view_triggers_row_activated",
G_CALLBACK(on_tree_view_triggers_row_activated), properties);
logo = gm_options_get(options, "logo");
if (logo != NULL) {
gtk_window_set_icon(GTK_WINDOW(properties->dialog),
gm_pixbuf_get(logo));
} else {
gtk_window_set_icon(GTK_WINDOW(properties->dialog),
gm_pixbuf_get("world.svg"));
}
}
#define G_WORLD_PROPERTIES_DIALOG_XML PACKAGE_DATA_DIR "/" PACKAGE \
"/ui/gm-world-properties.glade"
void
gm_world_properties_dialog_run_priv(GmWorld *world, gboolean is_new) {
GmWorldPropertiesDialog *properties =
gm_world_properties_dialog_find(world);
gchar *title;
if (properties) {
gtk_widget_show(properties->dialog);
gtk_window_present(GTK_WINDOW(properties->dialog));
return;
}
properties = g_new0(GmWorldPropertiesDialog, 1);
gm_world_properties_dialog_open = g_list_append(
gm_world_properties_dialog_open, properties);
properties->is_new = is_new;
properties->world = world;
properties->xml = glade_xml_new(G_WORLD_PROPERTIES_DIALOG_XML,
"gm_world_properties_dialog", NULL);
title = g_strconcat(_("World properties - "), gm_world_name(world), NULL);
properties->dialog = gm_world_properties_dialog_widget(properties,
"gm_world_properties_dialog");
properties->tree_view_triggers = gm_world_properties_dialog_widget(
properties, "tree_view_triggers");
properties->combo_box_charset = gm_world_properties_dialog_widget(
properties, "combo_box_charset");
gtk_window_set_title(GTK_WINDOW(properties->dialog), title);
g_free(title);
gm_world_properties_dialog_initialize(properties);
if (!properties->is_new) {
// Connect signal so we can close the dialog when a world gets removed
properties->handler_id = g_signal_connect(gm_app_instance(),
"world_removed",
G_CALLBACK(on_gm_world_properties_dialog_app_world_removed),
properties);
} else {
properties->handler_id = 0;
}
gm_world_properties_dialog_run_dialog(properties);
}
void
gm_world_properties_dialog_run(GmWorld *world) {
gm_world_properties_dialog_run_priv(world, FALSE);
}
void
gm_world_properties_dialog_run_new(GmWorld *world) {
if (!world) {
world = gm_world_new(NULL);
}
gm_world_properties_dialog_run_priv(world, TRUE);
}
gchar *
gm_world_properties_dialog_get_charset(gchar *charset) {
gchar *ch;
int i;
for (i = 0; i < (int)(sizeof(encodings) / sizeof(encoding)); i++) {
ch = g_strconcat(encodings[i].charset, " - ", _(encodings[i].name),
NULL);
if (g_strcasecmp(charset, ch) == 0) {
g_free(ch);
return g_strdup(encodings[i].charset);
}
g_free(ch);
}
return g_strdup(charset);
}
void
gm_world_properties_dialog_free_triggers(GmWorldPropertiesDialog *properties) {
GtkTreeView *tree_view = GTK_TREE_VIEW(properties->tree_view_triggers);
GtkTreeModel *store = gtk_tree_view_get_model(tree_view);
GtkTreeIter iter;
GmTrigger *t;
if (gtk_tree_model_get_iter_first(store, &iter)) {
do {
gtk_tree_model_get(store, &iter, 2, &t, -1);
gm_trigger_free(t);
} while (gtk_tree_model_iter_next(store, &iter));
}
}
void
gm_world_properties_dialog_set_triggers(GmWorldPropertiesDialog *properties) {
GtkTreeView *tree_view = GTK_TREE_VIEW(properties->tree_view_triggers);
GtkTreeModel *store = gtk_tree_view_get_model(tree_view);
GtkTreeIter iter;
GmTrigger *t;
GmTriggers *triggers = gm_world_triggers(properties->world);
gm_triggers_clear(triggers);
if (gtk_tree_model_get_iter_first(store, &iter)) {
do {
gtk_tree_model_get(store, &iter, 2, &t, -1);
gm_triggers_add(triggers, t);
} while (gtk_tree_model_iter_next(store, &iter));
}
}
gboolean
gm_world_properties_dialog_check_values(GmWorldPropertiesDialog *properties) {
GmWorld *same_world;
GmOptions *options = gm_world_options(properties->world);
GtkWidget *dialog;
GtkEntry *entry_name = GTK_ENTRY(gm_world_properties_dialog_widget(
properties, "entry_name"));
gchar *name = g_strdup(gtk_entry_get_text(entry_name));
GtkEntry *entry_host = GTK_ENTRY(gm_world_properties_dialog_widget(
properties, "entry_host"));
gchar *host = g_strdup(gtk_entry_get_text(entry_host));
g_strstrip(name);
g_strstrip(host);
same_world = gm_app_world_by_name(gm_app_instance(), name);
if (strlen(name) == 0) {
dialog = gtk_message_dialog_new(GTK_WINDOW(properties->dialog),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
"Name can not be empty, please fill in a name.");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
gtk_notebook_set_current_page(GTK_NOTEBOOK(
gm_world_properties_dialog_widget(properties,
"notebook_main")), 0);
gtk_widget_grab_focus(GTK_WIDGET(entry_name));
g_free(name);
g_free(host);
return FALSE;
}
if (same_world && same_world != properties->world) {
dialog = gtk_message_dialog_new(GTK_WINDOW(properties->dialog),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
_("Name can not be %s because a world with that name "
"already exists."),
name);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
gtk_notebook_set_current_page(GTK_NOTEBOOK(
gm_world_properties_dialog_widget(properties,
"notebook_main")), 0);
gtk_widget_grab_focus(GTK_WIDGET(entry_name));
g_free(name);
g_free(host);
return FALSE;
}
if (strlen(host) == 0) {
dialog = gtk_message_dialog_new(GTK_WINDOW(properties->dialog),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
_("Host can not be empty, please fill in a host."));
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
gtk_notebook_set_current_page(GTK_NOTEBOOK(
gm_world_properties_dialog_widget(properties,
"notebook_main")), 0);
gtk_widget_grab_focus(GTK_WIDGET(entry_host));
g_free(name);
g_free(host);
return FALSE;
}
gm_world_properties_dialog_set_triggers(properties);
gm_options_set(options, "host", host);
gm_options_set_int(options, "port", gtk_spin_button_get_value_as_int(
GTK_SPIN_BUTTON(gm_world_properties_dialog_widget(properties,
"spin_button_port"))));
gm_options_set_int(options, "reconnect", gtk_toggle_button_get_active(
GTK_TOGGLE_BUTTON(gm_world_properties_dialog_widget(properties,
"check_button_auto_reconnect"))));
gm_options_set_int(options, "autoload", gtk_toggle_button_get_active(
GTK_TOGGLE_BUTTON(gm_world_properties_dialog_widget(properties,
"check_button_auto_load"))));
gm_options_set(options, "player_name", (gchar *)gtk_entry_get_text(
GTK_ENTRY(gm_world_properties_dialog_widget(properties,
"entry_player_name"))));
gm_options_set(options, "password", (gchar *)gtk_entry_get_text(
GTK_ENTRY(gm_world_properties_dialog_widget(properties,
"entry_password"))));
gm_options_set(options, "charset", gm_world_properties_dialog_get_charset(
(gchar *)gtk_entry_get_text(GTK_ENTRY(GTK_BIN(
properties->combo_box_charset)->child))));
// Only change the name when it has actually changed, when the name
// changes the options and triggers are automatically saved to the new
// world location
if (gm_options_get(options, "name") == NULL ||
strcmp(name, gm_options_get(options, "name")) != 0) {
gm_options_set(options, "name", name);
} else {
// Now we need to save the options and triggers ourselfs
gm_options_save(options);
gm_triggers_save(gm_world_triggers(properties->world));
}
g_free(name);
g_free(host);
return TRUE;
}
void
gm_world_properties_dialog_run_dialog(GmWorldPropertiesDialog *properties) {
g_signal_connect(properties->dialog, "response",
G_CALLBACK(on_gm_world_properties_dialog_response), properties);
gtk_widget_show(GTK_WIDGET(properties->dialog));
}
/* CALLBACKS */
void
on_gm_world_properties_dialog_response(GtkDialog *dialog, gint response,
GmWorldPropertiesDialog *properties) {
gboolean is_okay = TRUE;
switch (response) {
case GTK_RESPONSE_OK:
is_okay = gm_world_properties_dialog_check_values(properties);
break;
default:
gm_world_properties_dialog_free_triggers(properties);
break;
}
if (!is_okay) {
return;
}
if (properties->is_new) {
if (response == GTK_RESPONSE_OK) {
gm_app_add_world(gm_app_instance(), properties->world);
} else {
g_object_unref(properties->world);
}
}
gtk_widget_destroy(properties->dialog);
if (properties->handler_id > 0) {
g_signal_handler_disconnect(gm_app_instance(), properties->handler_id);
}
g_object_unref(properties->xml);
gm_world_properties_dialog_open = g_list_remove(
gm_world_properties_dialog_open, properties);
g_free(properties);
}
void
on_button_add_trigger_clicked(GtkButton *button,
GmWorldPropertiesDialog *properties) {
GmTrigger *trigger = gm_triggers_dialog_run_new(properties->world, NULL);
if (trigger != NULL) {
gm_world_properties_dialog_add_trigger(properties, trigger);
}
}
GmTrigger *
gm_world_properties_dialog_selected_trigger(GmWorldPropertiesDialog *properties,
GtkTreeIter *iter) {
GtkTreeView *tree_view = GTK_TREE_VIEW(properties->tree_view_triggers);
GtkTreeModel *model = gtk_tree_view_get_model(tree_view);
GmTrigger *result = NULL;
if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(tree_view),
&model, iter)) {
gtk_tree_model_get(model, iter, 2, &result, -1);
}
return result;
}
void
on_button_edit_trigger_clicked(GtkButton *button,
GmWorldPropertiesDialog *properties) {
GtkTreeIter iter;
GmTrigger *trigger, *newt;
trigger = gm_world_properties_dialog_selected_trigger(properties, &iter);
if (trigger) {
newt = gm_triggers_dialog_run(properties->world, trigger);
if (newt) {
gm_world_properties_dialog_update_trigger(properties, &iter,
trigger);
}
} else {
gm_error_dialog(_("Select a trigger to edit first"),
GTK_WINDOW(properties->dialog));
}
}
void
on_button_delete_trigger_clicked(GtkButton *button,
GmWorldPropertiesDialog *properties) {
GtkTreeIter iter;
GmTrigger *trigger;
trigger = gm_world_properties_dialog_selected_trigger(properties, &iter);
if (trigger) {
gm_world_properties_dialog_remove_trigger(properties, &iter);
gm_trigger_free(trigger);
} else {
gm_error_dialog(_("First select a trigger to remove"),
GTK_WINDOW(properties->dialog));
}
}
void
on_tree_view_triggers_row_activated(GtkTreeView *treeview, GtkTreePath *arg1,
GtkTreeViewColumn *arg2, GmWorldPropertiesDialog *properties) {
on_button_edit_trigger_clicked(NULL, properties);
}
void
on_gm_world_properties_dialog_app_world_removed(GmApp *app, GmWorld *world,
GmWorldPropertiesDialog *properties) {
if (properties->world == world) {
gtk_dialog_response(GTK_DIALOG(properties->dialog), GTK_RESPONSE_CLOSE);
}
}

View File

@ -0,0 +1,17 @@
#ifndef __GM_WORLD_PROPERTIES_DIALOG__
#define __GM_WORLD_PROPERTIES_DIALOG__
#include <gtk/gtk.h>
#include <glade/glade.h>
#include "../gm-world.h"
typedef struct _encoding {
const gchar *charset;
const gchar *name;
} encoding;
void gm_world_properties_dialog_run(GmWorld *world);
void gm_world_properties_dialog_run_new(GmWorld *world);
#endif /* __GM_WORLD_PROPERTIES_DIALOG__ */

View File

@ -0,0 +1,466 @@
#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 <libgnomeui/libgnomeui.h>
#include "../gm-support.h"
#include "../gm-debug.h"
#include "../gm-app.h"
#include "gm-worlds-list-dialog.h"
#include "gm-world-properties-dialog.h"
#include "../gm-pixbuf.h"
typedef struct _GmWorldsListDialog {
GladeXML *xml;
GtkWidget *dialog;
GtkTreeView *tree_view_worlds;
GtkTreeModel *model_worlds;
} GmWorldsListDialog;
void on_gm_worlds_list_dialog_button_delete_clicked(GtkButton * utton,
gpointer user_data);
void on_gm_worlds_list_dialog_button_new_clicked(GtkButton *button,
gpointer user_data);
void on_gm_worlds_list_dialog_button_modify_clicked(GtkButton *button,
gpointer user_data);
void on_gm_worlds_list_dialog_button_duplicate_clicked(GtkButton *button,
gpointer user_data);
void on_gm_worlds_list_dialog_button_connect_clicked(GtkButton *button,
gpointer user_data);
void on_gm_worlds_list_dialog_tree_view_worlds_key_press(GtkWidget *widget,
GdkEventKey *event, gpointer user_data);
void on_gm_worlds_list_dialog_app_world_added(GmApp *app, GmWorld *world,
gpointer user_data);
void on_gm_worlds_list_dialog_app_world_removed(GmApp *app, GmWorld *world,
gpointer user_data);
void on_gm_worlds_list_dialog_world_option_changed(GmOptions *options,
const gchar *key, GmWorld *world);
void on_gm_worlds_list_dialog_response(GtkDialog *dialog, gint response,
gpointer user_data);
static GmWorldsListDialog *gm_worlds_list_dialog_instance;
enum {
LOGO_COLUMN,
NAME_COLUMN,
WORLD_COLUMN,
N_COLUMNS
};
GtkWidget *
gm_worlds_list_dialog_widget(gchar *name) {
return glade_xml_get_widget(gm_worlds_list_dialog_instance->xml, name);
}
gchar *
gm_worlds_list_dialog_world_text(GmWorld *world) {
gchar *text;
const gchar *player, *server;
GmOptions *options = gm_world_options(world);
player = gm_options_get(options, "player_name");
server = gm_options_get(options, "host");
text = g_strconcat("<b>", gm_options_get(options, "name"),
_("</b>\nServer: "),
((server && server[0] != '\0') ? server : _("<i>unspecified</i>")),
_("\nPlayer: "),
((player && player[0] != '\0') ? player : _("<i>unspecified</i>")),
NULL);
return text;
}
static void
gm_worlds_list_dialog_add_world(GmWorld *world) {
GtkTreeIter iter;
GdkPixbuf *pix_logo = NULL;
gchar *name = gm_worlds_list_dialog_world_text(world);
const gchar *logo = gm_options_get(gm_world_options(world), "logo");
GtkListStore *store = GTK_LIST_STORE(
gm_worlds_list_dialog_instance->model_worlds);
if (logo) {
pix_logo = gm_pixbuf_get_at_size(logo, 32, 32);
} else {
pix_logo = gm_pixbuf_get_at_size("world.svg", 32, 32);
}
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter, LOGO_COLUMN, pix_logo, NAME_COLUMN,
name, WORLD_COLUMN, world, -1);
g_signal_connect(gm_world_options(world), "option_changed",
G_CALLBACK(on_gm_worlds_list_dialog_world_option_changed), world);
g_free(name);
}
#define G_WORLDS_LISTS_DIALOG_XML PACKAGE_DATA_DIR "/" PACKAGE \
"/ui/gm-worlds-list.glade"
static void
gm_worlds_list_dialog_populate_worlds() {
GmApp *app = gm_app_instance();
GList *worlds = gm_app_worlds(app);
GList *item;
GmWorld *world;
for (item = worlds; item; item = item->next) {
world = (GmWorld *)(item->data);
gm_worlds_list_dialog_add_world(world);
}
g_list_free(worlds);
}
static void
gm_worlds_list_dialog_create_tree_view_worlds() {
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkTreeView *tree_view;
GtkTreeModel *model;
model = GTK_TREE_MODEL(gtk_list_store_new(N_COLUMNS, GDK_TYPE_PIXBUF,
G_TYPE_STRING, G_TYPE_POINTER));
tree_view = GTK_TREE_VIEW(gm_worlds_list_dialog_widget("tree_view_worlds"));
gtk_tree_view_set_model(tree_view, model);
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(tree_view),
GTK_SELECTION_MULTIPLE);
renderer = gtk_cell_renderer_pixbuf_new();
column = gtk_tree_view_column_new_with_attributes(_("Logo"), renderer,
"pixbuf", LOGO_COLUMN, NULL);
gtk_tree_view_column_set_min_width(column, 40);
gtk_tree_view_append_column(tree_view, column);
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer,
"markup", NAME_COLUMN, NULL);
gtk_tree_view_append_column(tree_view, column);
gm_worlds_list_dialog_instance->tree_view_worlds = tree_view;
gm_worlds_list_dialog_instance->model_worlds = model;
gm_worlds_list_dialog_populate_worlds();
}
void
gm_worlds_list_dialog_run() {
GladeXML *xml;
if (gm_worlds_list_dialog_instance) {
gtk_widget_show(gm_worlds_list_dialog_instance->dialog);
gtk_window_present(GTK_WINDOW(gm_worlds_list_dialog_instance->dialog));
return;
}
gm_worlds_list_dialog_instance = g_new0(GmWorldsListDialog, 1);
xml = glade_xml_new(G_WORLDS_LISTS_DIALOG_XML, "gm_worlds_list_dialog",
NULL);
gm_worlds_list_dialog_instance->xml = xml;
gm_worlds_list_dialog_create_tree_view_worlds();
glade_xml_signal_connect(xml, "on_button_delete_clicked",
G_CALLBACK(on_gm_worlds_list_dialog_button_delete_clicked));
glade_xml_signal_connect(xml, "on_button_new_clicked",
G_CALLBACK(on_gm_worlds_list_dialog_button_new_clicked));
glade_xml_signal_connect(xml, "on_button_modify_clicked",
G_CALLBACK(on_gm_worlds_list_dialog_button_modify_clicked));
glade_xml_signal_connect(xml, "on_button_duplicate_clicked",
G_CALLBACK(on_gm_worlds_list_dialog_button_connect_clicked));
glade_xml_signal_connect(xml, "on_button_connect_clicked",
G_CALLBACK(on_gm_worlds_list_dialog_button_connect_clicked));
glade_xml_signal_connect(xml, "on_tree_view_worlds_key_press",
G_CALLBACK(on_gm_worlds_list_dialog_tree_view_worlds_key_press));
gm_worlds_list_dialog_instance->dialog =
gm_worlds_list_dialog_widget("gm_worlds_list_dialog");
g_signal_connect(gm_app_instance(), "world_added",
G_CALLBACK(on_gm_worlds_list_dialog_app_world_added), NULL);
g_signal_connect(gm_app_instance(), "world_removed",
G_CALLBACK(on_gm_worlds_list_dialog_app_world_removed), NULL);
gtk_window_set_icon(GTK_WINDOW(gm_worlds_list_dialog_instance->dialog),
gm_pixbuf_get("world.svg"));
gtk_widget_show(gm_worlds_list_dialog_instance->dialog);
g_signal_connect(gm_worlds_list_dialog_instance->dialog, "response",
G_CALLBACK(on_gm_worlds_list_dialog_response), NULL);
}
static void
gm_worlds_list_dialog_delete_selected_worlds() {
GtkTreeView *tree_view = gm_worlds_list_dialog_instance->tree_view_worlds;
GtkTreeModel *model = gm_worlds_list_dialog_instance->model_worlds;
GtkTreeSelection *selection = gtk_tree_view_get_selection(tree_view);
GtkTreeIter iter;
GList *paths = gtk_tree_selection_get_selected_rows(selection, &model);
GList *refs = NULL;
GList *node = NULL;
gchar *message;
gpointer proxy = g_object_newv(G_TYPE_OBJECT, 0, NULL);
GmWorld *world;
for (node = paths; node; node = node->next) {
refs = g_list_append(refs,
gtk_tree_row_reference_new_proxy(proxy, model, node->data));
gtk_tree_path_free(node->data);
}
g_list_free(paths);
if (refs) {
for (node = refs; node; node = node->next) {
GtkTreePath *path = gtk_tree_row_reference_get_path(node->data);
gtk_tree_model_get_iter(model, &iter, path);
gtk_tree_model_get(model, &iter, WORLD_COLUMN, &world, -1);
if (!(world && gm_world_loaded(world))) {
gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
gm_app_remove_world(gm_app_instance(), world);
} else {
message = g_strdup_printf(_
("Can't remove the world %s because it is loaded. "
"First close the world and then try again."),
gm_world_name(world));
gm_error_dialog(message,
GTK_WINDOW(gm_worlds_list_dialog_instance->dialog));
g_free(message);
}
gtk_tree_row_reference_deleted(proxy, path);
gtk_tree_path_free(path);
gtk_tree_row_reference_free(node->data);
}
} else {
gm_error_dialog(_("You first need to select a world to delete."),
GTK_WINDOW(gm_worlds_list_dialog_instance->dialog));
}
g_list_free(refs);
g_object_unref(proxy);
}
void
gm_worlds_list_dialog_modify_world() {
GtkTreeView *tree_view = gm_worlds_list_dialog_instance->tree_view_worlds;
GtkTreeModel *model = gm_worlds_list_dialog_instance->model_worlds;
GtkTreeSelection *selection = gtk_tree_view_get_selection(tree_view);
GtkTreeIter iter;
GList *first_path = gtk_tree_selection_get_selected_rows(selection, &model);
GmWorld *world;
if (first_path) {
gtk_tree_model_get_iter(model, &iter, first_path->data);
gtk_tree_model_get(model, &iter, WORLD_COLUMN, &world, -1);
gm_world_properties_dialog_run(world);
} else {
gm_error_dialog(_("You first need to select a world to modify."),
GTK_WINDOW(gm_worlds_list_dialog_instance->dialog));
}
g_list_free(first_path);
}
GList *
gm_worlds_list_dialog_selected_path() {
GtkTreeView *tree_view = gm_worlds_list_dialog_instance->tree_view_worlds;
GtkTreeModel *model = gm_worlds_list_dialog_instance->model_worlds;
GtkTreeSelection *selection = gtk_tree_view_get_selection(tree_view);
return gtk_tree_selection_get_selected_rows(selection, &model);
}
GmWorld *
gm_worlds_list_dialog_selected_world() {
GtkTreeIter iter;
GList *first_path = gm_worlds_list_dialog_selected_path();
GmWorld *world = NULL;
if (first_path) {
gtk_tree_model_get_iter(gm_worlds_list_dialog_instance->model_worlds,
&iter, first_path->data);
gtk_tree_model_get(gm_worlds_list_dialog_instance->model_worlds, &iter,
WORLD_COLUMN, &world, -1);
}
g_list_free(first_path);
return world;
}
gboolean
gm_worlds_list_dialog_find(GmWorld *world, GtkTreeIter *iter) {
GtkTreeModel *model = gm_worlds_list_dialog_instance->model_worlds;
GmWorld *list_world;
if (gtk_tree_model_get_iter_first(model, iter)) {
do {
gtk_tree_model_get(model, iter, WORLD_COLUMN, &list_world, -1);
if (world == list_world) {
return TRUE;
}
} while (gtk_tree_model_iter_next(model, iter));
}
return FALSE;
}
// Callbacks
void
on_gm_worlds_list_dialog_response(GtkDialog *dialog, gint response,
gpointer user_data) {
g_object_unref(gm_worlds_list_dialog_instance->xml);
gtk_widget_destroy(gm_worlds_list_dialog_instance->dialog);
g_free(gm_worlds_list_dialog_instance);
gm_worlds_list_dialog_instance = NULL;
}
void
on_gm_worlds_list_dialog_button_delete_clicked(GtkButton *button,
gpointer user_data) {
gm_worlds_list_dialog_delete_selected_worlds();
}
void
on_gm_worlds_list_dialog_tree_view_worlds_key_press(GtkWidget *widget,
GdkEventKey *event, gpointer user_data) {
switch (event->keyval) {
case GDK_D: case GDK_d:
gm_worlds_list_dialog_delete_selected_worlds();
break;
case GDK_O: case GDK_o:
break;
case GDK_N: case GDK_n:
gm_world_properties_dialog_run_new(NULL);
break;
case GDK_M: case GDK_m:
gm_worlds_list_dialog_modify_world();
break;
}
}
void
on_gm_worlds_list_dialog_button_new_clicked(GtkButton *button,
gpointer user_data) {
gm_world_properties_dialog_run_new(NULL);
}
void
on_gm_worlds_list_dialog_button_modify_clicked(GtkButton *button,
gpointer user_data) {
gm_worlds_list_dialog_modify_world();
}
void
on_gm_worlds_list_dialog_button_duplicate_clicked(GtkButton *button,
gpointer user_data) {
GmWorld *world = gm_worlds_list_dialog_selected_world();
GmWorld *new_world;
if (world) {
// now duplicate the world
new_world = gm_world_dup(world);
// Yeah we got some world, now show the props!
gm_world_properties_dialog_run_new(new_world);
} else {
gm_error_dialog(
_("You first need to select a world to duplicate."),
GTK_WINDOW(gm_worlds_list_dialog_instance->dialog));
}
}
void
on_gm_worlds_list_dialog_button_connect_clicked(GtkButton *button,
gpointer user_data) {
GList *first_path = gm_worlds_list_dialog_selected_path();
GList *path;
GmWorld *world;
GtkTreeIter iter;
if (first_path) {
for (path = first_path; path; path = path->next) {
gtk_tree_model_get_iter(
gm_worlds_list_dialog_instance->model_worlds, &iter,
first_path->data);
gtk_tree_model_get(gm_worlds_list_dialog_instance->model_worlds,
&iter, WORLD_COLUMN, &world, -1);
gm_world_load(world);
}
} else {
gm_error_dialog(
_("You first need to select a world to connect to."),
GTK_WINDOW(gm_worlds_list_dialog_instance->dialog));
}
g_list_free(first_path);
}
void
on_gm_worlds_list_dialog_app_world_added(GmApp *app, GmWorld *world,
gpointer user_data) {
gm_worlds_list_dialog_add_world(world);
}
void
on_gm_worlds_list_dialog_app_world_removed(GmApp *app, GmWorld *world,
gpointer user_data) {
GtkTreeIter iter;
if (gm_worlds_list_dialog_find(world, &iter)) {
gtk_list_store_remove(GTK_LIST_STORE(
gm_worlds_list_dialog_instance->model_worlds), &iter);
}
}
void
on_gm_worlds_list_dialog_world_option_changed(GmOptions *options,
const gchar *key, GmWorld *world) {
GtkTreeIter iter;
GtkListStore *store =
GTK_LIST_STORE(gm_worlds_list_dialog_instance->model_worlds);
gchar *text;
const gchar *logo;
GdkPixbuf *pix_logo;
if (!gm_worlds_list_dialog_find(world, &iter)) {
return;
}
if (strcmp(key, "name") == 0 ||
strcmp(key, "player_name") == 0 ||
strcmp(key, "host") == 0) {
text = gm_worlds_list_dialog_world_text(world);
gtk_list_store_set(store, &iter, NAME_COLUMN, text, -1);
g_free(text);
} else if (strcmp(key, "logo")) {
logo = gm_options_get(options, "logo");
if (logo) {
pix_logo = gm_pixbuf_get_at_size(logo, 32, 32);
} else {
pix_logo = gm_pixbuf_get_at_size("world.svg", 32, 32);
}
gtk_list_store_set(store, &iter, LOGO_COLUMN, pix_logo, -1);
}
}

View File

@ -0,0 +1,6 @@
#ifndef __GM_WORLDS_LIST_DIALOG__
#define __GM_WORLDS_LIST_DIALOG__
void gm_worlds_list_dialog_run();
#endif /* __GM_WORLDS_LIST_DIALOG */

47
gnoemoe/genclass.rb Normal file
View File

@ -0,0 +1,47 @@
#!/usr/bin/ruby
def replaceAll(line, subst)
subst.each do |sub,rep|
line = line.gsub('{' + sub + '}', rep)
end
return line
end
def degenerateCaps(text)
text = text.gsub(/[A-Z]+[a-z]+/) do |s|
s.downcase + '_'
end
return text[0..-2]
end
subst = {}
name = ARGV[0]
if (name == nil) then
print "Class name (capitalized): "
name = gets[0..-2]
end
subst['template_'] = degenerateCaps(name)
subst['template-'] = subst['template_'].sub('_', '-')
subst['TEMPLATE'] = subst['template_'].upcase
subst['Template'] = name
tc = IO.readlines("template.c")
th = IO.readlines("template.h")
f = File.open('gm-' + subst['template-'] + '.c', 'w')
tc.each do |line|
f.write(replaceAll(line, subst))
end
f.close
f = File.open('gm-' + subst['template-'] + '.h', 'w')
th.each do |line|
f.write(replaceAll(line, subst))
end
f.close
print "Done ...\n"

58
gnoemoe/gengtkclass.rb Executable file
View File

@ -0,0 +1,58 @@
#!/usr/bin/ruby
def replaceAll(line, subst)
subst.each do |sub,rep|
line = line.gsub('{' + sub + '}', rep)
end
return line
end
def degenerateCaps(text)
text = text.gsub(/[A-Z]+[a-z]+/) do |s|
s.downcase + '_'
end
return text[0..-2]
end
subst = {}
name = ARGV[0]
parent = ARGV[1]
if (name == nil) then
print "Class name (capitalized): "
name = gets[0..-2]
end
subst['template_'] = degenerateCaps(name)
subst['template-'] = subst['template_'].sub('_', '-')
subst['TEMPLATE'] = subst['template_'].upcase
subst['Template'] = name
if (parent == nil) then
print "Parent name (capitalized): "
parent = gets[0..-2]
end
subst['parent_'] = degenerateCaps(parent)
subst['parent-'] = subst['parent_'].sub('_', '-')
subst['PARENT'] = subst['parent_'].upcase
subst['Parent'] = parent
tc = IO.readlines("gtktemplate.c")
th = IO.readlines("gtktemplate.h")
f = File.open('gm-' + subst['template-'] + '.c', 'w')
tc.each do |line|
f.write(replaceAll(line, subst))
end
f.close
f = File.open('gm-' + subst['template-'] + '.h', 'w')
th.each do |line|
f.write(replaceAll(line, subst))
end
f.close
print "Done ...\n"

559
gnoemoe/gm-app.c Normal file
View File

@ -0,0 +1,559 @@
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#include <gtk/gtk.h>
#include <libgnome/libgnome.h>
#include <libgnomevfs/gnome-vfs.h>
#include <libgnomeui/libgnomeui.h>
#include "widgets/gm-app-view.h"
#include "gm-world.h"
#include "gm-color-table.h"
#include "gm-app.h"
//#include "editor.h"
#include "gm-debug.h"
#include "gm-pixbuf.h"
#include "gm-support.h"
#include "gm-scripts.h"
static gchar *debug_level = NULL;
static gboolean show_version = FALSE;
static gboolean recover = FALSE;
static gchar *load_worlds = NULL;
static GmApp *application;
//static void gm_app_tray_create(tray_info *t);
//gboolean on_gm_app_tray_button_press(GtkWidget *widget, GdkEventButton *event,
// gpointer user_data);
//gboolean on_gm_app_tray_destroy(GtkWidget *widget, gpointer user_data);
void on_gm_app_view_size_allocate(GmAppView *view, GtkAllocation *allocation,
GmApp *app);
struct poptOption poptions[] = {
{"debug", 'd', POPT_ARG_STRING, &debug_level, 0, N_("Enable debugging. "
"Specify multiple debug levels with a comma seperated list. "
"Available levels: default, mcp, all"), N_("DBGGLEVEL")},
{"version", 'v', POPT_ARG_NONE, &show_version, 0, N_("Show application "
"version"), NULL},
{"load", 'l', POPT_ARG_STRING, &load_worlds, 0, N_("Load specified worlds. "
"Specify multiple worlds with a comma separated list"),
N_("WORLDS")},
{"recover", 'r', POPT_ARG_NONE, &recover, 0, N_("Recover from previous "
"session (used with gnome session)"), NULL},
{NULL, '\0', 0, NULL, 0, NULL, NULL}
};
#define GM_APP_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), \
GM_TYPE_APP, GmAppPrivate))
void gm_app_destroy_worlds(GmApp *app);
struct _GmAppPrivate {
gchar *path;
gchar *worlds_path;
gchar *options_path;
GmAppView *view;
GmOptions *options;
GmColorTable *color_table;
GnomeClient *client;
GList *worlds;
#ifdef HAVE_RUBY
GmScripts *scripts;
#endif
//tray_info tray;
};
/* Signals */
enum {
WORLD_ADDED,
WORLD_REMOVED,
NUM_SIGNALS
};
static guint app_signals[NUM_SIGNALS] = {0};
G_DEFINE_TYPE(GmApp, gm_app, G_TYPE_OBJECT)
static void
gm_app_finalize(GObject *object) {
GmApp *app = GM_APP(object);
gnome_vfs_shutdown();
//mcp_fini();
//editor_fini();
#ifdef HAVE_RUBY
g_object_unref(app->priv->scripts);
#endif
//mcpconsole_fini();
gm_app_destroy_worlds(app);
gm_pixbuf_fini();
gm_options_save(app->priv->options);
g_object_unref(app->priv->options);
gm_color_table_save(app->priv->color_table);
g_object_unref(app->priv->color_table);
g_free(app->priv->path);
g_free(app->priv->worlds_path);
g_free(app->priv->options_path);
G_OBJECT_CLASS(gm_app_parent_class)->finalize(object);
}
static void
gm_app_class_init(GmAppClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = gm_app_finalize;
app_signals[WORLD_ADDED] =
g_signal_new("world_added",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmAppClass, world_added),
NULL, NULL,
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE,
1,
G_TYPE_POINTER);
app_signals[WORLD_REMOVED] =
g_signal_new("world_removed",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmAppClass, world_removed),
NULL, NULL,
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE,
1,
G_TYPE_POINTER);
g_type_class_add_private(object_class, sizeof(GmAppPrivate));
}
static void
gm_app_init(GmApp *app) {
app->priv = GM_APP_GET_PRIVATE(app);
app->priv->path = NULL;
app->priv->worlds_path = NULL;
app->priv->options_path = NULL;
app->priv->options = NULL;
app->priv->client = NULL;
app->priv->worlds = NULL;
//gm_app_tray_create(&(app->priv->tray));
}
/* Private functions */
void
gm_app_destroy_worlds(GmApp *app) {
// TODO
}
static void
on_gm_app_session_die(GnomeClient * client, gpointer client_data) {
gtk_main_quit();
}
/*static void
gm_app_tray_create(tray_info *t) {
t->flash_timeout = 0;
t->icon = egg_tray_icon_new(_("GnoeMoe Gnome MOO Client"));
t->event_box = gtk_event_box_new();
t->iconnr = TRAY_ICON_DEFAULT;
t->image = gtk_image_new_from_pixbuf(
gnoe_pixbuf_get("tray/default.svg"));
gtk_container_add(GTK_CONTAINER(t->event_box), t->image);
t->tooltips = gtk_tooltips_new();
gtk_widget_show(t->event_box);
gtk_widget_show(t->image);
gtk_container_add(GTK_CONTAINER(t->icon), t->event_box);
gtk_widget_show(GTK_WIDGET(t->icon));
gtk_widget_add_events(GTK_WIDGET(t->icon), GDK_BUTTON_PRESS_MASK);
g_signal_connect(t->icon, "button_press_event",
G_CALLBACK(on_gm_app_tray_button_press), NULL);
// Handles when the area is removed from the panel.
g_signal_connect(t->icon, "destroy", G_CALLBACK(on_gm_app_tray_destroy),
t->event_box);
}*/
static gboolean
on_gm_app_save_session(GnomeClient * client, gint phase,
GnomeSaveStyle save_style, gint is_shutdown,
GnomeInteractStyle interact_style, gint is_fast, gchar ** client_data) {
gchar **argv;
gint argc = sizeof(client_data) / sizeof(gchar *) + 1;
int i;
GString *ws = g_string_new("");
GList *elem, *list;
GmWorld *world;
argv = g_new(gchar *, argc);
argv[0] = client_data[0];
argv[1] = "--recover";
for (i = 1; i < argc - 1; i++) {
argv[i + 1] = client_data[i];
}
gnome_client_set_clone_command(client, argc, argv);
gnome_client_set_restart_command(client, argc, argv);
// Saving worlds state
list = gm_app_worlds(application);
for (elem = list; elem; elem = elem->next) {
world = (GmWorld *) (elem->data);
if (gm_world_loaded(world)) {
if (strlen(ws->str) != 0) {
ws = g_string_append_c(ws, ';');
}
ws = g_string_append(ws, gm_world_name(world));
}
}
g_list_free(list);
gm_options_set(gm_app_options(application), "worlds_saved_state", ws->str);
gm_options_save(gm_app_options(application));
g_string_free(ws, TRUE);
return TRUE;
}
void
gm_app_create_settings(GmApp *app) {
app->priv->options = gm_options_new();
gm_options_set(app->priv->options, "editor_alternative", "0");
gm_options_set(app->priv->options, "editor_embed", "0");
gm_options_set(app->priv->options, "editor_needs_terminal", "0");
gm_options_set(app->priv->options, "worlds_saved_state", "");
gm_options_set(app->priv->options, "search_direction", "1");
}
void
gm_app_load_worlds(GmApp *app, gboolean autoload) {
GDir *handle = g_dir_open(app->priv->worlds_path, 0, NULL);
char *name, *path;
GmWorld *new_world;
if (handle != NULL) {
while ((name = (char *) g_dir_read_name(handle)) != NULL) {
path = g_strconcat(app->priv->worlds_path, G_DIR_SEPARATOR_S,
name, NULL);
if (g_file_test(path, G_FILE_TEST_IS_DIR)) {
new_world = gm_world_new(path);
if (new_world != NULL) {
gm_app_add_world(app, new_world);
if (autoload && gm_options_get_int(
gm_world_options(new_world), "autoload")) {
gm_world_load(new_world);
}
}
} else {
gm_debug_msg(DEBUG_ALWAYS, "Nee: %s", path);
}
g_free(path);
}
g_dir_close(handle);
} else {
gm_debug_msg(DEBUG_DEFAULT, "GmApp.load_worlds: failed to open worlds path %s",
app->priv->path);
}
}
void
gm_app_initialize(GmApp *app) {
gchar *colors_path;
gm_debug_set_level(debug_level);
app->priv->worlds_path = g_strconcat(app->priv->path, G_DIR_SEPARATOR_S,
"worlds", NULL);
app->priv->options_path = g_strconcat(app->priv->path, G_DIR_SEPARATOR_S,
"settings", NULL);
if (!g_file_test(app->priv->path, G_FILE_TEST_EXISTS)) {
mkdir(app->priv->path, 0755);
mkdir(app->priv->worlds_path, 0755);
}
#ifdef HAVE_RUBY
app->priv->scripts = gm_scripts_new();
#endif
gm_app_create_settings(app);
gm_options_load(app->priv->options, app->priv->options_path);
// Load color table
colors_path = g_strconcat(app->priv->path, G_DIR_SEPARATOR_S, "colors",
NULL);
app->priv->color_table = gm_color_table_new_from_options(colors_path);
g_free(colors_path);
}
void
gm_app_run(GmApp *app) {
gchar **wrlds;
const gchar *savedState;
int i = 0;
GmWorld *world;
int width, height;
app->priv->view = gm_app_view_new(app);
width = gm_options_get_int(app->priv->options, "width");
height = gm_options_get_int(app->priv->options, "height");
if (height > 10 && width > 10) {
gtk_window_set_default_size(GTK_WINDOW(app->priv->view), width, height);
}
gtk_widget_show(GTK_WIDGET(app->priv->view));
//mcpconsole_init();
g_signal_connect(app->priv->view, "size_allocate",
G_CALLBACK(on_gm_app_view_size_allocate), app);
#ifdef HAVE_RUBY
gm_scripts_load(app->priv->scripts);
#endif
gm_app_load_worlds(app, !(recover || load_worlds));
if (recover) {
savedState = gm_options_get(app->priv->options, "worlds_saved_state");
if (strlen(savedState) != 0) {
wrlds = g_strsplit(savedState, ";", -1);
for (i = 0; wrlds[i]; i++) {
if (strlen(wrlds[i]) != 0) {
world = gm_app_world_by_name(app, wrlds[i]);
if (world) {
gm_world_load(world);
}
}
}
g_strfreev(wrlds);
}
} else if (load_worlds) {
wrlds = g_strsplit(load_worlds, ",", -1);
for (i = 0; wrlds[i]; i++) {
world = gm_app_world_by_name(app, wrlds[i]);
if (world) {
gm_world_load(world);
}
}
g_strfreev(wrlds);
}
gtk_main();
}
/* Public functions */
GmApp *
gm_app_new(int argc, char *argv[]) {
GmApp *app = GM_APP(g_object_new(GM_TYPE_APP, NULL));
app->priv->path = gnome_util_home_file("gnoemoe");
if (!app->priv->path) {
printf(_("GnoeMoe Application: there is no application directory, "
"this is very bad!!!\n"));
return app;
}
#ifdef ENABLE_NLS
bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
textdomain(GETTEXT_PACKAGE);
#endif
gtk_set_locale();
gtk_init(&argc, &argv);
gnome_program_init(PACKAGE, VERSION, LIBGNOMEUI_MODULE,
argc, argv, GNOME_PARAM_POPT_TABLE, poptions,
GNOME_PARAM_APP_DATADIR, PACKAGE_DATA_DIR, NULL);
if (show_version) {
printf(_("Current version of GnoeMoe is %s\n"), VERSION);
return app;
}
app->priv->client = gnome_master_client();
gtk_signal_connect(GTK_OBJECT(app->priv->client), "save_yourself",
GTK_SIGNAL_FUNC(on_gm_app_save_session), argv);
gtk_signal_connect(GTK_OBJECT(app->priv->client), "die",
GTK_SIGNAL_FUNC(on_gm_app_session_die), NULL);
/* Initialize everything */
gnome_vfs_init();
glade_init();
//mcp_init();
gm_pixbuf_init();
gm_app_initialize(app);
//editor_init();
return app;
}
void
gm_app_add_world(GmApp *app, GmWorld *world) {
app->priv->worlds = g_list_append(app->priv->worlds, g_object_ref(world));
g_signal_emit(app, app_signals[WORLD_ADDED], 0, world);
}
void
gm_app_remove_world(GmApp *app, GmWorld *world) {
// Only remove when not loaded
const gchar *path = gm_world_path(world);
if (!gm_world_loaded(world)) {
app->priv->worlds = g_list_remove(app->priv->worlds, world);
g_signal_emit(app, app_signals[WORLD_REMOVED], 0, world);
g_object_unref(world);
gm_directory_remove_all(path, TRUE);
}
}
GmWorld *
gm_app_world_by_name(GmApp *app, gchar *name) {
GList *elem;
const gchar *world_name;
for (elem = app->priv->worlds; elem; elem = elem->next) {
world_name = gm_world_name(GM_WORLD(elem->data));
if (!g_strcasecmp(name, world_name)) {
return GM_WORLD(elem->data);
}
}
return NULL;
}
GmOptions *
gm_app_options(GmApp *app) {
return app->priv->options;
}
#ifdef HAVE_RUBY
GmScripts *
gm_app_scripts(GmApp *app) {
return app->priv->scripts;
}
#endif
const gchar *
gm_app_worlds_path(GmApp *app) {
return app->priv->worlds_path;
}
const gchar *
gm_app_path(GmApp *app) {
return app->priv->path;
}
GList *
gm_app_worlds(GmApp *app) {
return g_list_copy(app->priv->worlds);
}
GmColorTable *
gm_app_color_table(GmApp *app) {
return app->priv->color_table;
}
GmApp *
gm_app_instance() {
return application;
}
int
main(int argc, char *argv[]) {
g_type_init();
application = gm_app_new(argc, argv);
gm_app_run(application);
g_object_unref(application);
return 0;
}
/* Callbacks */
void
on_gm_app_view_size_allocate(GmAppView *view, GtkAllocation *allocation,
GmApp *app) {
gm_options_set_int(app->priv->options, "width", allocation->width);
gm_options_set_int(app->priv->options, "height", allocation->height);
}
/*gboolean
on_gm_app_tray_button_press(GtkWidget *widget, GdkEventButton *event,
gpointer user_data) {
if (event->type == GDK_2BUTTON_PRESS ||
event->type == GDK_3BUTTON_PRESS) {
return FALSE;
}
switch (event->button) {
case 1:
if_main_show_hide(!GTK_WIDGET_VISIBLE(wndMain));
break;
default:
return FALSE;
}
return TRUE;
}
gboolean
on_gm_app_tray_destroy(GtkWidget *widget, gpointer user_data) {
gtk_widget_destroy(GTK_WIDGET(app_tray_info.icon));
app_tray_info.icon = NULL;
app_tray_info.event_box = NULL;
app_tray_info.image = NULL;
app_tray_info.tooltips = NULL;
if (app_tray_info.flash_timeout != 0) {
g_source_remove(app_tray_info.flash_timeout);
}
tray_create(&app_tray_info);
if (!have_tray()) {
gtk_widget_show(if_main_get_widget("wndMain"));
}
return TRUE;
}*/

79
gnoemoe/gm-app.h Normal file
View File

@ -0,0 +1,79 @@
#ifndef __GM_APP_H__
#define __GM_APP_H__
//#define G_XML PACKAGE_DATA_DIR "/" PACKAGE "/ui/gm-main.glade"
#include <gtk/gtk.h>
#include "gm-world.h"
#include "gm-options.h"
#include "gm-color-table.h"
#include "gm-scripts.h"
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_APP (gm_app_get_type())
#define GM_APP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
GM_TYPE_APP, GmApp))
#define GM_APP_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
GM_TYPE_APP, GmApp const))
#define GM_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
GM_TYPE_APP, GmAppClass))
#define GM_IS_APP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
GM_TYPE_APP))
#define GM_IS_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
GM_TYPE_APP))
#define GM_APP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
GM_TYPE_APP, GmAppClass))
/* Private structure type */
typedef struct _GmAppPrivate GmAppPrivate;
/*
* Main object structure
*/
typedef struct _GmApp GmApp;
struct _GmApp {
GObject object;
/*< private > */
GmAppPrivate *priv;
};
/*
* Class definition
*/
typedef struct _GmAppClass GmAppClass;
struct _GmAppClass {
GObjectClass parent_class;
/* Signals */
void (* world_added) (GmApp *app, GmWorld *world);
void (* world_removed) (GmApp *app, GmWorld *world);
};
GmApp *gm_app_instance();
GType gm_app_get_type(void) G_GNUC_CONST;
GmApp *gm_app_new(int argc, char *argv[]);
void gm_app_add_world(GmApp *app, GmWorld *world);
void gm_app_remove_world(GmApp *app, GmWorld *world);
const gchar *gm_app_worlds_path(GmApp *app);
const gchar *gm_app_path(GmApp *app);
GmOptions *gm_app_options(GmApp *app);
GList *gm_app_worlds(GmApp *app);
GmColorTable *gm_app_color_table(GmApp *app);
GmWorld *gm_app_world_by_name(GmApp *app, gchar *name);
#ifdef HAVE_RUBY
GmScripts *gm_app_scripts(GmApp *app);
#endif
G_END_DECLS
#endif /* __GM_APP_H__ */

4
gnoemoe/gm-bogus.h Normal file
View File

@ -0,0 +1,4 @@
#ifndef __GM_BOGUS_H__
#define __GM_BOGUS_H__
#endif /* __GM_BOGUS_H__ */

359
gnoemoe/gm-color-table.c Normal file
View File

@ -0,0 +1,359 @@
#include <glib.h>
#include "gm-color-table.h"
#include "string.h"
#include "ansi.h"
#include "gm-debug.h"
#define GM_COLOR_TABLE_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_COLOR_TABLE, GmColorTablePrivate))
void gm_color_table_item_free(gpointer item);
typedef struct _GmColorTableItem GmColorTableItem;
struct _GmColorTableItem {
gchar *hex;
GdkColor color;
};
typedef struct _GmColorTableSchemeItem {
const gchar *name;
const gchar *hex;
} GmColorTableSchemeItem;
static const GmColorTableSchemeItem scheme_default[] = {
{"fg_default", "#000000"},
{"fg_black", "#000000"},
{"fg_red", "#663822"},
{"fg_green", "#445632"},
{"fg_yellow", "#D1940C"},
{"fg_blue", "#314E6C"},
{"fg_purple", "#494066"},
{"fg_cyan", "#0000FFFFFFFF"},
{"fg_white", "#BAB5AB"},
{"fg_default_h", "#565248"},
{"fg_black_h", "#565248"},
{"fg_red_h", "#990000"},
{"fg_green_h", "#267726"},
{"fg_yellow_h", "#EED680"},
{"fg_blue_h", "#9DB8D2"},
{"fg_purple_h", "#ADA7C8"},
{"fg_cyan_h", "#86EEFFFFFFFF"},
{"fg_white_h", "#807D74"},
//{"bg_default", "#EAE8E3"},
{"bg_default", "#FFFFFF"},
{"bg_black", "#000000"},
{"bg_red", "#663822"},
{"bg_green", "#445632"},
{"bg_yellow", "#D1940C"},
{"bg_blue", "#314E6C"},
{"bg_purple", "#494066"},
{"bg_cyan", "#0000FFFFFFFF"},
{"bg_white", "#FFFFFFFFFFFF"},
{NULL, NULL}
};
static const GmColorTableSchemeItem scheme_white_on_black[] = {
{"fg_default", "#D6B5D6B5D6B5"},
{"fg_black", "#2D6B2D6B2D6B"},
{"fg_red", "#FFFF00000000"},
{"fg_green", "#0000FFFF0000"},
{"fg_yellow", "#FFFFD0450000"},
{"fg_blue", "#3EF73EF7BFFF"},
{"fg_purple", "#A0A02020F0F0"},
{"fg_cyan", "#0000FFFFFFFF"},
{"fg_white", "#D8C5D8C5D8C5"},
{"fg_default_h", "#FFFFFFFFFFFF"},
{"fg_black_h", "#529452945294"},
{"fg_red_h", "#FFFF785F785F"},
{"fg_green_h", "#66ADFFFF66AD"},
{"fg_yellow_h", "#FFFFFFFF58C6"},
{"fg_blue_h", "#86318631FFFF"},
{"fg_purple_h", "#C6576A18FFFF"},
{"fg_cyan_h", "#86EEFFFFFFFF"},
{"fg_white_h", "#FFFFFFFFFFFF"},
{"bg_default", "#000000000000"},
{"bg_black", "#2B5B2B5B2B5B"},
{"bg_red", "#FFFF00000000"},
{"bg_green", "#000080000000"},
{"bg_yellow", "#C047C0470000"},
{"bg_blue", "#00000000FFFF"},
{"bg_purple", "#A0A02020F0F0"},
{"bg_cyan", "#0000B74CB74C"},
{"bg_white", "#FFFFFFFFFFFF"},
{NULL, NULL}
};
typedef struct _GmColorScheme {
GmColorTableScheme scheme;
const gchar *name;
const GmColorTableSchemeItem *values;
} GmColorScheme;
static const GmColorScheme scheme_names[] = {
{SCHEME_NONE, "none", NULL},
{SCHEME_DEFAULT, "default", scheme_default},
{SCHEME_WHITE_ON_BLACK, "white_on_black", scheme_white_on_black},
{SCHEME_USER, "user", NULL},
{SCHEME_NONE, NULL, NULL}
};
struct _GmColorTablePrivate {
GHashTable *colors;
gchar *font_description;
GmOptions *options;
GmColorTableScheme scheme;
};
/* Signals */
enum {
COLOR_CHANGED,
FONT_CHANGED,
NUM_SIGNALS
};
static guint color_table_signals[NUM_SIGNALS] = {0};
G_DEFINE_TYPE(GmColorTable, gm_color_table, G_TYPE_OBJECT)
static void
gm_color_table_finalize(GObject *object) {
//GmColorTable *table = GM_COLOR_TABLE(object);
G_OBJECT_CLASS(gm_color_table_parent_class)->finalize(object);
}
static void
gm_color_table_class_init(GmColorTableClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = gm_color_table_finalize;
color_table_signals[COLOR_CHANGED] =
g_signal_new("color_changed",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmColorTableClass, color_changed),
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE,
1,
G_TYPE_STRING);
color_table_signals[FONT_CHANGED] =
g_signal_new("font_changed",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmColorTableClass, font_changed),
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE,
1,
G_TYPE_STRING);
g_type_class_add_private(object_class, sizeof(GmColorTablePrivate));
}
static void
gm_color_table_init(GmColorTable *table) {
table->priv = GM_COLOR_TABLE_GET_PRIVATE(table);
table->priv->colors = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, gm_color_table_item_free);
table->priv->scheme = SCHEME_NONE;
}
/* Private functions */
void
gm_color_table_item_free(gpointer item) {
GmColorTableItem *i = (GmColorTableItem *)(item);
g_free(i->hex);
g_free(i);
}
void
gm_color_table_load_scheme(GmColorTable *table,
GmColorTableScheme scheme) {
int i = 0;
const GmColorTableSchemeItem *values = scheme_names[scheme].values;
while (values[i].name != NULL) {
gm_color_table_set(table, values[i].name, values[i].hex);
++i;
}
table->priv->scheme = scheme;
}
void
gm_color_table_initialize(GmColorTable *table) {
gm_color_table_set_font_description(table, "Monospace 10");
gm_color_table_load_scheme(table, SCHEME_DEFAULT);
}
void
gm_color_table_fill_from_options(GmColorTable *table) {
unsigned int i;
const gchar *value;
GmOptions *options = table->priv->options;
// New, color schemes
value = gm_options_get(options, "color_scheme");
if (value == NULL || strcmp(value, "user") == 0) {
for (i = 0; i < sizeof(ansi_colors) / sizeof(ansinamepair); i++) {
value = gm_options_get(options, ansi_colors[i].name);
if (value != NULL) {
gm_color_table_set(table, ansi_colors[i].name, value);
}
}
} else {
gm_color_table_set_from_scheme_name(table, value);
}
value = gm_options_get(options, "font_family");
if (value && *value != '\0') {
gm_color_table_set_font_description(table, value);
} else {
gm_options_set(options, "font_family", "Monospace 10");
}
}
/* Public functions */
GmColorTable *
gm_color_table_new(void) {
GmColorTable *table = GM_COLOR_TABLE(g_object_new(GM_TYPE_COLOR_TABLE, NULL));
gm_color_table_load_scheme(table, SCHEME_DEFAULT);
return table;
}
GmColorTable *
gm_color_table_new_from_options(gchar *filename) {
GmColorTable *table = GM_COLOR_TABLE(g_object_new(GM_TYPE_COLOR_TABLE, NULL));
table->priv->options = gm_options_new();
gm_options_set(table->priv->options, "color_scheme", "default");
gm_options_load(table->priv->options, filename);
gm_color_table_fill_from_options(table);
return table;
}
void gm_color_table_save(GmColorTable *table) {
if (table->priv->options) {
gm_options_save(table->priv->options);
}
}
void
gm_color_table_set(GmColorTable *table, const gchar *name, const gchar *hex) {
GmColorTableItem *item;
item = g_hash_table_lookup(table->priv->colors, name);
if (table->priv->scheme == SCHEME_USER) {
gm_options_set(table->priv->options, name, hex);
}
if (!item) {
item = g_new0(GmColorTableItem, 1);
g_hash_table_insert(table->priv->colors, g_strdup(name), item);
}
if (item->hex == NULL || strcmp(hex, item->hex) != 0) {
g_free(item->hex);
item->hex = g_strdup(hex);
gdk_color_parse(item->hex, &(item->color));
g_signal_emit(table, color_table_signals[COLOR_CHANGED], 0, name);
}
}
gboolean
gm_color_table_get(GmColorTable *table, const gchar *name, GdkColor *color) {
GmColorTableItem *item;
item = g_hash_table_lookup(table->priv->colors, name);
if (item != NULL) {
*color = item->color;
return TRUE;
} else {
return FALSE;
}
}
const gchar *
gm_color_table_get_hex(GmColorTable *table, const gchar *name) {
GmColorTableItem *item;
item = g_hash_table_lookup(table->priv->colors, name);
if (item != NULL) {
return item->hex;
} else {
return FALSE;
}
}
void
gm_color_table_set_font_description(GmColorTable *table,
const gchar *font_description) {
const gchar *fd;
if (font_description == NULL) {
fd = "Monospace 10";
} else {
fd = font_description;
}
gm_options_set(table->priv->options, "font_family",
fd);
if (table->priv->font_description == NULL ||
strcmp(table->priv->font_description, fd) != 0) {
g_free(table->priv->font_description);
table->priv->font_description = g_strdup(fd);
g_signal_emit(table, color_table_signals[FONT_CHANGED], 0,
table->priv->font_description);
}
}
const gchar *
gm_color_table_font_description(GmColorTable *table) {
return table->priv->font_description;
}
void
gm_color_table_set_from_scheme_name(GmColorTable *table, const gchar *scheme) {
int i = 0;
while (scheme_names[i].name != NULL) {
if (strcasecmp(scheme_names[i].name, scheme) == 0 &&
scheme_names[i].values != NULL) {
gm_color_table_load_scheme(table, scheme_names[i].scheme);
break;
}
++i;
}
if (scheme_names[i].name == NULL) {
gm_color_table_load_scheme(table, SCHEME_DEFAULT);
gm_options_set(table->priv->options, "color_scheme",
"default");
} else {
gm_options_set(table->priv->options, "color_scheme",
scheme_names[i].name);
}
}

79
gnoemoe/gm-color-table.h Normal file
View File

@ -0,0 +1,79 @@
#ifndef __GM_COLOR_TABLE_H__
#define __GM_COLOR_TABLE_H__
#include <gtk/gtk.h>
#include "gm-options.h"
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_COLOR_TABLE (gm_color_table_get_type())
#define GM_COLOR_TABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_COLOR_TABLE, GmColorTable))
#define GM_COLOR_TABLE_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_COLOR_TABLE, GmColorTable const))
#define GM_COLOR_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_COLOR_TABLE, GmColorTableClass))
#define GM_IS_COLOR_TABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_COLOR_TABLE))
#define GM_IS_COLOR_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_COLOR_TABLE))
#define GM_COLOR_TABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_COLOR_TABLE, GmColorTableClass))
/* Private structure type */
typedef struct _GmColorTablePrivate GmColorTablePrivate;
/*
* Main object structure
*/
typedef struct _GmColorTable GmColorTable;
struct _GmColorTable {
GObject object;
/*< private > */
GmColorTablePrivate *priv;
};
/*
* Class definition
*/
typedef struct _GmColorTableClass GmColorTableClass;
struct _GmColorTableClass {
GObjectClass parent_class;
/* Signals */
void (* color_changed) (GmColorTable *table, const gchar *color);
void (* font_changed) (GmColorTable *table, const gchar *font_description);
};
typedef enum _GmColorTableScheme {
SCHEME_NONE = 0,
SCHEME_DEFAULT = 1,
SCHEME_WHITE_ON_BLACK = 2,
SCHEME_USER = 3
} GmColorTableScheme;
GType gm_color_table_get_type(void) G_GNUC_CONST;
GmColorTable *gm_color_table_new(void);
GmColorTable *gm_color_table_new_from_options(gchar *filename);
void gm_color_table_save(GmColorTable *table);
void gm_color_table_set(GmColorTable *table, const gchar *name,
const gchar *hex);
gboolean gm_color_table_get(GmColorTable *table, const gchar *name,
GdkColor *color);
const gchar *gm_color_table_get_hex(GmColorTable *table, const gchar *name);
void gm_color_table_set_font_description(GmColorTable *table,
const gchar *font_description);
const gchar *gm_color_table_font_description(GmColorTable *table);
void gm_color_table_load_scheme(GmColorTable *table,
GmColorTableScheme scheme);
void gm_color_table_set_from_scheme_name(GmColorTable *table,
const gchar *name);
void gm_color_table_set_from_options(GmColorTable *table, GmOptions *options);
G_END_DECLS
#endif /* __GM_COLOR_TABLE_H__ */

77
gnoemoe/gm-debug.c Normal file
View File

@ -0,0 +1,77 @@
#include <time.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <glib.h>
#include "gm-debug.h"
typedef struct _LevelMap {
gchar const *name;
GmDebugLevel level;
} LevelMap;
static LevelMap level_mapping[] = {
{"default", DEBUG_DEFAULT},
{"mcp", DEBUG_MCP},
{"all", DEBUG_ALL},
{NULL, 0}
};
static gint debug_level = DEBUG_ALWAYS;
static void
gm_debug_msg_real(FILE *f, struct tm *timet, gchar *line, va_list args) {
fprintf(f, "[%02d:%02d:%02d] # ", timet->tm_hour, timet->tm_min,
timet->tm_sec);
vfprintf(f, line, args);
fprintf(f, "\n");
}
void
gm_debug_msg(gint level, gchar *line, ...) {
struct tm *timet;
time_t timer;
va_list args;
if ((debug_level | level) == debug_level) {
va_start(args, line);
timer = time(0);
timet = localtime(&timer);
if (level & DEBUG_ALWAYS) {
gm_debug_msg_real(stdout, timet, line, args);
}
if ((level & DEBUG_ALWAYS) != level) {
gm_debug_msg_real(stderr, timet, line, args);
}
va_end(args);
}
}
void
gm_debug_set_level(gchar *level) {
gchar **levels, **iter;
debug_level = DEBUG_ALWAYS;
LevelMap *map;
if (level == NULL) {
return;
}
levels = g_strsplit(level, ",", -1);
for (iter = levels; *iter; ++iter) {
for (map = level_mapping; map->name; ++map) {
if (strcasecmp(map->name, *iter) == 0) {
debug_level = debug_level | map->level;
break;
}
}
}
g_strfreev(levels);
}

14
gnoemoe/gm-debug.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef __GM_DEBUG_H__
#define __GM_DEBUG_H__ 1
typedef enum _GmDebugLevel {
DEBUG_ALWAYS = 1 << 1,
DEBUG_DEFAULT = 1 << 2,
DEBUG_MCP = 1 << 3,
DEBUG_ALL = 0xFFFF
} GmDebugLevel;
void gm_debug_msg(gint level, char *line, ...);
void gm_debug_set_level(gchar *level);
#endif /* __GM_DEBUG_H__ */

128
gnoemoe/gm-editor.c Normal file
View File

@ -0,0 +1,128 @@
#include <string.h>
#include "gm-editor.h"
#include "gm-world.h"
#include "gm-support.h"
#define GM_EDITOR_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_EDITOR, GmEditorPrivate))
struct _GmEditorPrivate {
gchar *name;
gchar *upload_cmd;
gchar *mcp_type;
GmEditType type;
gboolean is_code;
GList *lines;
};
/* Signals */
enum {
SAVE,
NUM_SIGNALS
};
static guint editor_signals[NUM_SIGNALS] = {0};
G_DEFINE_TYPE(GmEditor, gm_editor, G_TYPE_OBJECT)
static void
gm_editor_finalize(GObject *object) {
GmEditor *editor = GM_EDITOR(object);
g_free(editor->priv->name);
g_free(editor->priv->upload_cmd);
g_free(editor->priv->mcp_type);
g_list_free_simple(editor->priv->lines);
G_OBJECT_CLASS(gm_editor_parent_class)->finalize(object);
}
static void
gm_editor_class_init(GmEditorClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = gm_editor_finalize;
editor_signals[SAVE] =
g_signal_new("save",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmEditorClass, save),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
g_type_class_add_private(object_class, sizeof(GmEditorPrivate));
}
static void
gm_editor_init(GmEditor *view) {
view->priv = GM_EDITOR_GET_PRIVATE(view);
}
GmEditor *
gm_editor_new(gchar *name, gchar *uploadcmd, GList *text) {
GmEditor *editor = GM_EDITOR(g_object_new(GM_TYPE_EDITOR, NULL));
editor->priv->name = g_strdup(name);
editor->priv->upload_cmd = g_strdup(uploadcmd);
editor->priv->mcp_type = NULL;
editor->priv->lines = g_list_copy(text);
editor->priv->type = E_LEGACY;
editor->priv->is_code = (strncmp(uploadcmd, "@program", 7) == 0);
return editor;
}
GmEditor *
gm_editor_new_mcp(gchar *name, gchar *reference, gchar *type,
GList *text) {
GmEditor *editor = GM_EDITOR(g_object_new(GM_TYPE_EDITOR, NULL));
editor->priv->name = g_strdup(name);
editor->priv->upload_cmd = g_strdup(reference);
editor->priv->mcp_type = g_strdup(type);
editor->priv->lines = g_list_copy(text);
editor->priv->type = E_LEGACY;
editor->priv->is_code = (strcmp(type, "moo-code") == 0);
return editor;
}
gboolean
gm_editor_is_code(GmEditor *editor) {
return editor->priv->is_code;
}
gchar *
gm_editor_name(GmEditor *editor) {
return editor->priv->name;
}
gchar *
gm_editor_upload_cmd(GmEditor *editor) {
return editor->priv->upload_cmd;
}
gchar *
gm_editor_mcp_type(GmEditor *editor) {
return editor->priv->mcp_type;
}
GList *
gm_editor_lines(GmEditor *editor) {
return editor->priv->lines;
}
GmEditType
gm_editor_type(GmEditor *editor) {
return editor->priv->type;
}
void
gm_editor_save(GmEditor *editor) {
g_signal_emit(editor, editor_signals[SAVE], 0);
}

68
gnoemoe/gm-editor.h Normal file
View File

@ -0,0 +1,68 @@
#ifndef __GM_EDITOR_H__
#define __GM_EDITOR_H__
#include <glib-object.h>
#include <glib.h>
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_EDITOR (gm_editor_get_type())
#define GM_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_EDITOR, GmEditor))
#define GM_EDITOR_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_EDITOR, GmEditor const))
#define GM_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_EDITOR, GmEditorClass))
#define GM_IS_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_EDITOR))
#define GM_IS_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_EDITOR))
#define GM_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_EDITOR, GmEditorClass))
/* Private structure type */
typedef struct _GmEditorPrivate GmEditorPrivate;
/*
* Main object structure
*/
typedef struct _GmEditor GmEditor;
struct _GmEditor {
GObject object;
/*< private > */
GmEditorPrivate *priv;
};
/*
* Class definition
*/
typedef struct _GmEditorClass GmEditorClass;
struct _GmEditorClass {
GObject parent_class;
/* Signals */
void (* save) (GmEditor *editor);
};
typedef enum _GmEditType GmEditType;
enum _GmEditType {
E_LEGACY,
E_MCP
};
GType gm_editor_get_type(void) G_GNUC_CONST;
GmEditor *gm_editor_new(gchar *name, gchar *uploadcmd, GList *text);
GmEditor *gm_editor_new_mcp(gchar *name, gchar *reference, gchar *type,
GList *text);
void gm_editor_save(GmEditor *editor);
gboolean gm_editor_is_code(GmEditor *editor);
gchar *gm_editor_name(GmEditor *editor);
gchar *gm_editor_upload_cmd(GmEditor *editor);
gchar *gm_editor_mcp_type(GmEditor *editor);
GList *gm_editor_lines(GmEditor *editor);
GmEditType gm_editor_type(GmEditor *editor);
G_END_DECLS
#endif /* __GM_EDITOR_H__ */

4
gnoemoe/gm-marshal.list Normal file
View File

@ -0,0 +1,4 @@
VOID:STRING,UINT
VOID:STRING,INT
VOID:INT,INT
VOID:INT,STRING

537
gnoemoe/gm-net.c Normal file
View File

@ -0,0 +1,537 @@
#include <glib.h>
#include <sys/time.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include "gm-net.h"
#include "gm-marshal.h"
#include "gm-debug.h"
#include "gm-support.h"
#define GM_NET_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_NET, GmNetPrivate))
struct _GmNetPrivate {
int socket; /**< the connection socket, is -1 if not connected */
GIOChannel *channel; /**< the channel which is used to 'listen' on the socket via the glib main loop */
guint source; /**< the id of the socket watch */
guint connect_timeout_id; /**< the connect timeout id */
guint connect_check_id; /**< the connect timeout id */
struct timeval last_connected; /**< contains when the last connect happened */
int tn_last; /**< used for telnet */
int tn_subneg; /**< used for telnet */
struct addrinfo *addr;
struct addrinfo *current;
GmNetState state; /**< state of the connection */
gchar *current_host;
gchar *current_port;
};
/* Signals */
enum {
STATE_CHANGING,
NET_ERROR,
BYTES_RECV,
NUM_SIGNALS
};
static guint net_signals[NUM_SIGNALS] = {0};
G_DEFINE_TYPE(GmNet, gm_net, G_TYPE_OBJECT)
/* These values taken from RFC 854 and RFC 857. */
#define TN_WILL 251
#define TN_WONT 252
#define TN_DO 253
#define TN_DONT 254
#define TN_IAC 255 /* Interpret As Command */
#define TN_SB 250 /* start of subnegotiation */
#define TN_SE 240 /* end of subnegotiaton */
void gm_net_connect_next(GmNet *net);
gboolean on_gm_net_input_recv(GIOChannel * source, GIOCondition condition,
GmNet *net);
gboolean on_gm_net_connect_check(GIOChannel * source, GIOCondition condition,
GmNet *net);
gboolean on_gm_net_connect_timeout(GmNet *net);
static void
gm_net_finalize(GObject *object) {
//GmNet *net = GM_NET(object);
G_OBJECT_CLASS(gm_net_parent_class)->finalize(object);
}
static void
gm_net_class_init(GmNetClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = gm_net_finalize;
net_signals[STATE_CHANGING] =
g_signal_new("state_changing",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmNetClass, state_changing),
NULL, NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE,
1,
G_TYPE_UINT);
net_signals[NET_ERROR] =
g_signal_new("net_error",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmNetClass, net_error),
NULL, NULL,
gm_marshal_VOID__STRING_INT,
G_TYPE_NONE,
2,
G_TYPE_STRING,
G_TYPE_INT);
net_signals[BYTES_RECV] =
g_signal_new("bytes_recv",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmNetClass, bytes_recv),
NULL, NULL,
gm_marshal_VOID__STRING_UINT,
G_TYPE_NONE,
2,
G_TYPE_STRING,
G_TYPE_UINT);
g_type_class_add_private(object_class, sizeof(GmNetPrivate));
}
static void
gm_net_init(GmNet *net) {
net->priv = GM_NET_GET_PRIVATE(net);
net->priv->state = GM_NET_STATE_DISCONNECTED;
net->priv->addr = NULL;
net->priv->current = NULL;
net->priv->channel = NULL;
net->priv->source = 0;
net->priv->connect_timeout_id = 0;
net->priv->connect_check_id = 0;
}
void
gm_net_set_state(GmNet *net, GmNetState state) {
g_signal_emit(net, net_signals[STATE_CHANGING], 0, state);
net->priv->state = state;
}
void
gm_net_clean_disconnection(GmNet *net) {
GError *err = NULL;
if (!net->priv->channel) {
gm_debug_msg(DEBUG_DEFAULT, "GmNet.CleanDisconnection: NOT clean for %d",
net->priv->socket);
return;
}
gm_debug_msg(DEBUG_DEFAULT, "GmNet.CleanDisconnection: clean disconnect for %d",
net->priv->socket);
// Shutdown the channel
g_io_channel_shutdown(net->priv->channel, TRUE, &err);
if (err) {
gm_debug_msg(DEBUG_DEFAULT, "GmNet.CleanDisconnection: error on channel shutdown: "
"%s", err->message);
g_error_free(err);
err = NULL;
}
g_io_channel_unref(net->priv->channel);
net->priv->channel = NULL;
net->priv->socket = -1;
net->priv->tn_last = 0;
net->priv->tn_subneg = 0;
gm_net_set_state(net, GM_NET_STATE_DISCONNECTED);
}
void
gm_net_dirty_disconnection(GmNet *net, int err) {
gchar *msg;
// Pff, stupid, we print a message and pass it on to clean_disconnection
gm_debug_msg(DEBUG_DEFAULT, "GmNet.DirtyDisconnection: dirty disconnect %d",
net->priv->socket);
msg = g_strdup_printf(_("Connection lost... (%s)"), strerror(err));
g_signal_emit(net, net_signals[NET_ERROR], 0, msg,
GM_NET_ERROR_DISCONNECTED);
g_free(msg);
gm_net_clean_disconnection(net);
}
void
gm_net_connect_succeed(GmNet *net) {
freeaddrinfo(net->priv->addr);
net->priv->addr = NULL;
net->priv->current = NULL;
net->priv->source = g_io_add_watch(net->priv->channel,
G_IO_IN | G_IO_HUP, (GIOFunc)on_gm_net_input_recv, net);
if (net->priv->connect_timeout_id != 0) {
g_source_remove(net->priv->connect_timeout_id);
net->priv->connect_timeout_id = 0;
}
if (net->priv->connect_check_id != 0) {
g_source_remove(net->priv->connect_check_id);
net->priv->connect_check_id = 0;
}
gettimeofday(&(net->priv->last_connected), NULL);
gm_net_set_state(net, GM_NET_STATE_CONNECTED);
}
void
gm_net_connect_failed(GmNet *net, gchar *err, gint code) {
if (net->priv->channel) {
g_io_channel_shutdown(net->priv->channel, TRUE, NULL);
g_io_channel_unref(net->priv->channel);
net->priv->channel = NULL;
}
g_signal_emit(net, net_signals[NET_ERROR], 0, err, GM_NET_ERROR_CONNECTING);
if (net->priv->addr && net->priv->current->ai_next) {
net->priv->current = net->priv->current->ai_next;
gm_net_connect_next(net);
} else {
net->priv->socket = -1;
if (net->priv->addr) {
freeaddrinfo(net->priv->addr);
net->priv->addr = NULL;
net->priv->current = NULL;
}
// Don't use set_state here because we weren't really connected before
gm_net_set_state(net, GM_NET_STATE_DISCONNECTED);
}
}
void
gm_net_connect_next(GmNet *net) {
char host[NI_MAXHOST], port[NI_MAXSERV];
int ret, result;
struct addrinfo *tmp = net->priv->current;
if (tmp == NULL) {
return;
} else {
if ((ret = getnameinfo(tmp->ai_addr, tmp->ai_addrlen, host, NI_MAXHOST,
port, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
gm_debug_msg(DEBUG_DEFAULT, "GmNet.ConnectNext: getnameinfo error: %s",
gai_strerror(ret));
gm_net_connect_failed(net, (gchar *)gai_strerror(ret), ret);
return;
}
net->priv->current_host = host;
net->priv->current_port = port;
gm_net_set_state(net, GM_NET_STATE_TRY_ADDRESS);
net->priv->socket = socket(tmp->ai_family, tmp->ai_socktype,
tmp->ai_protocol);
if (net->priv->socket < 0) {
gm_net_connect_failed(net, strerror(errno), errno);
} else {
fcntl(net->priv->socket, F_SETFL,
fcntl(net->priv->socket, F_GETFL) | O_NONBLOCK);
if ((result = connect(net->priv->socket, tmp->ai_addr,
net->priv->addr->ai_addrlen)) == -1 && errno != EINPROGRESS) {
gm_net_connect_failed(net, strerror(errno), errno);
} else {
net->priv->channel = g_io_channel_unix_new(net->priv->socket);
g_io_channel_set_close_on_unref(net->priv->channel, TRUE);
if (result == 0) {
gm_net_connect_succeed(net);
} else {
net->priv->connect_check_id = g_io_add_watch(net->priv->channel,
G_IO_OUT|G_IO_ERR, (GIOFunc)on_gm_net_connect_check, net);
net->priv->connect_timeout_id = g_timeout_add(5000,
(GSourceFunc)on_gm_net_connect_timeout, net);
}
}
}
}
}
void
gm_net_handle_telnet(GmNet *net, unsigned char *buf, int *len) {
int i, j;
unsigned char c;
j = 0;
for (i = 0; i < *len; ++i) {
c = buf[i];
if (net->priv->tn_last) {
switch (net->priv->tn_last) {
case TN_WILL: case TN_WONT: case TN_DO: case TN_DONT:
net->priv->tn_last = 0;
break;
case TN_IAC:
switch (c) {
case TN_WILL: case TN_WONT: case TN_DO: case TN_DONT:
net->priv->tn_last = c;
break;
case TN_SB:
net->priv->tn_subneg = 1;
net->priv->tn_last = 0;
break;
case TN_SE:
net->priv->tn_subneg = 0;
net->priv->tn_last = 0;
break;
case TN_IAC:
if (!net->priv->tn_subneg) {
buf[j] = c;
++j;
}
net->priv->tn_last = 0;
break;
default:
net->priv->tn_last = 0;
break;
}
}
} else if (c == TN_IAC) {
net->priv->tn_last = TN_IAC;
} else if (net->priv->tn_subneg) {
continue;
} else {
buf[j] = c;
++j;
}
}
*len = j; //Since j-- is the last written char
}
/* Public */
GmNet *
gm_net_new() {
GmNet *net = GM_NET(g_object_new(GM_TYPE_NET, NULL));
return net;
}
GmNetState
gm_net_state(GmNet *net) {
return net->priv->state;
}
void
gm_net_connect(GmNet *net, const gchar *host, const gchar *port) {
struct addrinfo hint;
int ret;
char shost[NI_MAXHOST], sport[NI_MAXSERV];
if (net->priv->state != GM_NET_STATE_DISCONNECTED) {
return;
}
snprintf(shost, NI_MAXHOST - 1, "%s", host);
snprintf(sport, NI_MAXSERV - 1, "%s", port);
net->priv->current_host = shost;
net->priv->current_port = sport;
gm_net_set_state(net, GM_NET_STATE_CONNECTING);
memset(&hint, 0, sizeof(hint));
hint.ai_flags = 0;
hint.ai_family = AF_UNSPEC;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = IPPROTO_TCP;
gm_debug_msg(DEBUG_DEFAULT, "GmNet.Connect: getaddrinfo: %s : %s", shost, sport);
if ((ret = getaddrinfo(shost, sport, &hint, &(net->priv->addr))) != 0) {
net->priv->addr = NULL;
gm_debug_msg(DEBUG_DEFAULT, "GmNet.Connect: getaddrinfo failed: %s", gai_strerror(ret));
gm_net_connect_failed(net, (gchar *)gai_strerror(ret), ret);
return;
}
if (net->priv->addr != NULL) {
net->priv->current = net->priv->addr;
gm_net_connect_next(net);
} else {
gm_net_connect_failed(net, _("No addresses available"), 0);
}
}
void
gm_net_disconnect(GmNet *net) {
if (net->priv->state == GM_NET_STATE_CONNECTED) {
gm_net_set_state(net, GM_NET_STATE_DISCONNECTING);
// Remove the watch
g_source_remove(net->priv->source);
gm_net_clean_disconnection(net);
}
}
void
gm_net_send_line(GmNet *net, gchar *line) {
gchar *send_line;
send_line = (gchar *)(g_strconcat(line, "\r\n", NULL));
gm_net_send(net, send_line);
g_free(send_line);
}
void
gm_net_send(GmNet *net, gchar *text) {
int result;
fd_set connect_set;
if (net->priv->state == GM_NET_STATE_CONNECTED) {
gm_debug_msg(DEBUG_DEFAULT, "GmNet.Send: %s", text);
if ((result = send(net->priv->socket, text, strlen(text), 0)) == -1
&& (errno == EAGAIN || errno == EWOULDBLOCK)) {
FD_ZERO(&connect_set);
FD_SET(net->priv->socket, &connect_set);
// Wait for sending to be done
select(net->priv->socket + 1, NULL, &connect_set, NULL, NULL);
} else if (result == -1) {
gm_debug_msg(DEBUG_DEFAULT, "GmNet.Send: error on sending line: %s", strerror(errno));
gm_net_dirty_disconnection(net, errno);
}
} else {
g_signal_emit(net, net_signals[NET_ERROR], 0, _("Not connected"),
GM_NET_ERROR);
gm_debug_msg(DEBUG_DEFAULT, "GmNet.Send: not connected!");
}
}
const gchar *
gm_net_current_host(GmNet *net) {
return net->priv->current_host;
}
const gchar *
gm_net_current_port(GmNet *net) {
return net->priv->current_port;
}
/* Callbacks */
#define MAX_RECV_BUF 1024
gboolean
on_gm_net_input_recv(GIOChannel * source, GIOCondition condition, GmNet *net) {
unsigned char lbuf[MAX_RECV_BUF];
int len;
if (condition == G_IO_HUP) {
gm_net_set_state(net, GM_NET_STATE_DISCONNECTING);
gm_net_clean_disconnection(net);
return FALSE;
}
if (net->priv->state != GM_NET_STATE_CONNECTED) {
gm_debug_msg(DEBUG_DEFAULT, "GmNet.OnInputRecv: not connected!");
return FALSE;
}
// Break the received line by newline (skip \r)
len = recv(net->priv->socket, lbuf, MAX_RECV_BUF - 2, 0);
gm_debug_msg(DEBUG_DEFAULT, "GmNet.OnInputRecv: received %d bytes", len);
if (len < 1) {
// Disconnected, either clean or dirty
// (shouldn't this be caught by G_IO_HUP?)
if (len < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return TRUE;
}
gm_net_set_state(net, GM_NET_STATE_DISCONNECTING);
gm_net_dirty_disconnection(net, errno);
} else {
gm_net_set_state(net, GM_NET_STATE_DISCONNECTING);
gm_net_clean_disconnection(net);
}
return FALSE;
} else {
// It's fine, we have text!
gm_net_handle_telnet(net, lbuf, &len);
g_signal_emit(net, net_signals[BYTES_RECV], 0, lbuf, len);
}
return TRUE;
}
gboolean
on_gm_net_connect_check(GIOChannel * source, GIOCondition condition,
GmNet *net) {
int option = 0;
socklen_t optionsize = sizeof(option);
if (net->priv->connect_timeout_id != 0) {
g_source_remove(net->priv->connect_timeout_id);
net->priv->connect_timeout_id = 0;
}
if (condition == G_IO_ERR) {
getsockopt(net->priv->socket, SOL_SOCKET, SO_ERROR, &option, &optionsize);
gm_net_connect_failed(net, strerror(option), option);
} else {
gm_net_connect_succeed(net);
}
return FALSE;
}
gboolean
on_gm_net_connect_timeout(GmNet *net) {
net->priv->connect_timeout_id = 0;
if (net->priv->connect_check_id != 0) {
g_source_remove(net->priv->connect_check_id);
net->priv->connect_check_id = 0;
}
gm_net_connect_failed(net, _("Connect timeout (5)"), 0);
return FALSE;
}

81
gnoemoe/gm-net.h Normal file
View File

@ -0,0 +1,81 @@
#ifndef __GM_NET_H__
#define __GM_NET_H__
#include <glib.h>
#include <glib-object.h>
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_NET (gm_net_get_type())
#define GM_NET(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
GM_TYPE_NET, GmNet))
#define GM_NET_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
GM_TYPE_NET, GmNet const))
#define GM_NET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
GM_TYPE_NET, GmNetClass))
#define GM_IS_NET(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
GM_TYPE_NET))
#define GM_IS_NET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
GM_TYPE_NET))
#define GM_NET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
GM_TYPE_NET, GmNetClass))
typedef enum _GmNetState {
GM_NET_STATE_CONNECTED, /**< socket connected */
GM_NET_STATE_DISCONNECTED, /**< socket disconnected */
GM_NET_STATE_CONNECTING, /**< socket still connecting */
GM_NET_STATE_TRY_ADDRESS, /**< connecting to address */
GM_NET_STATE_DISCONNECTING /**< socket still disconnecting */
} GmNetState;
typedef enum _GmNetError {
GM_NET_ERROR_CONNECTING, /**< error while connecting */
GM_NET_ERROR_DISCONNECTED, /**< error while connecting */
GM_NET_ERROR /**< general error */
} GmNetError;
/* Private structure type */
typedef struct _GmNetPrivate GmNetPrivate;
/*
* Main object structure
*/
typedef struct _GmNet GmNet;
struct _GmNet {
GObject object;
/*< private > */
GmNetPrivate *priv;
};
/*
* Class definition
*/
typedef struct _GmNetClass GmNetClass;
struct _GmNetClass {
GObjectClass parent_class;
/* Signals */
void (* state_changing) (GmNet *net, guint state);
void (* net_error) (GmNet *net, gchar *error, gint code);
void (* bytes_recv) (GmNet *net, gchar *text);
};
GType gm_net_get_type(void) G_GNUC_CONST;
GmNet *gm_net_new(void);
GmNetState gm_net_state(GmNet *net);
void gm_net_connect(GmNet *net, const gchar *host, const gchar *port);
void gm_net_disconnect(GmNet *net);
void gm_net_send_line(GmNet *net, gchar *line);
void gm_net_send(GmNet *net, gchar *text);
const gchar *gm_net_current_host(GmNet *net);
const gchar *gm_net_current_port(GmNet *net);
G_END_DECLS
#endif /* __GM_NET_H__ */

212
gnoemoe/gm-options.c Normal file
View File

@ -0,0 +1,212 @@
#include <glib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "gm-options.h"
#include "gm-string.h"
#include "gm-debug.h"
extern int errno;
#define GM_OPTIONS_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_OPTIONS, GmOptionsPrivate))
struct _GmOptionsPrivate {
GHashTable *options;
gchar *filepath;
};
/* Signals */
enum {
OPTION_CHANGED,
NUM_SIGNALS
};
static guint options_signals[NUM_SIGNALS] = {0};
G_DEFINE_TYPE(GmOptions, gm_options, G_TYPE_OBJECT)
static void
gm_options_finalize(GObject *object) {
GmOptions *options = GM_OPTIONS(object);
g_hash_table_destroy(options->priv->options);
g_free(options->priv->filepath);
G_OBJECT_CLASS(gm_options_parent_class)->finalize(object);
}
static void
gm_options_class_init(GmOptionsClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = gm_options_finalize;
options_signals[OPTION_CHANGED] =
g_signal_new("option_changed",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmOptionsClass, option_changed),
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE,
1,
G_TYPE_STRING);
g_type_class_add_private(object_class, sizeof(GmOptionsPrivate));
}
static void
gm_options_init(GmOptions *options) {
options->priv = GM_OPTIONS_GET_PRIVATE(options);
options->priv->options = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, g_free);
options->priv->filepath = NULL;
}
GmOptions *
gm_options_new(void) {
GmOptions *options = GM_OPTIONS(g_object_new(GM_TYPE_OPTIONS, NULL));
return options;
}
void
gm_options_dup_option(gchar *key, gchar *value, GmOptions *copy) {
gm_options_set(copy, key, value);
}
GmOptions *
gm_options_dup(GmOptions *source) {
GmOptions *copy = gm_options_new();
g_hash_table_foreach(source->priv->options, (GHFunc)gm_options_dup_option,
copy);
return copy;
}
// Adds an option to opt, even if key already exists
void
gm_options_set(GmOptions * options, const gchar *key, const gchar *value) {
gchar *trimmed = gm_string_trim(key);
gchar *lookup = g_hash_table_lookup(options->priv->options, trimmed);
gboolean changed = lookup != NULL && lookup != value;
if (lookup != value) {
g_hash_table_insert(options->priv->options, g_strdup(trimmed),
g_strdup(value));
}
if (changed) {
g_signal_emit(options, options_signals[OPTION_CHANGED], 0, trimmed);
}
g_free(trimmed);
}
const gchar *
gm_options_get(GmOptions *options, const gchar *key) {
return g_hash_table_lookup(options->priv->options, key);
}
void
gm_options_set_int(GmOptions *options, const gchar *key, int value) {
gchar val[15];
g_snprintf((gchar *) &val, 15, "%d", value);
gm_options_set(options, key, (const gchar *)val);
}
int
gm_options_get_int(GmOptions *options, const gchar *key) {
const gchar *val = gm_options_get(options, key);
int ret;
if (val && gm_string_to_int(val, &ret)) {
return ret;
} else {
return 0;
}
}
void
gm_options_save_value(gchar *key, gchar *value, FILE *f) {
gm_debug_msg(DEBUG_DEFAULT, "GmOptions.SaveValue: saving %s, %s", key, value);
fprintf(f, "%s=%s\n", key, value);
}
void
gm_options_save(GmOptions *options) {
FILE *f;
if (options->priv->filepath == NULL) {
return;
}
f = fopen(options->priv->filepath, "w");
gm_debug_msg(DEBUG_DEFAULT, "GmOptions.save: saving options (%s)!", options->priv->filepath);
if (f) {
g_hash_table_foreach(options->priv->options,
(GHFunc)gm_options_save_value, f);
fclose(f);
chmod(options->priv->filepath, 0660);
} else {
gm_debug_msg(DEBUG_DEFAULT, "GmOptions.save: couldn't open option file for saving: %s",
strerror(errno));
}
}
void
gm_options_save_as(GmOptions *options, const gchar *filename) {
g_free(options->priv->filepath);
options->priv->filepath = g_strdup(filename);
gm_options_save(options);
}
void
gm_options_load(GmOptions *options, const char *filename) {
FILE *f;
gchar **keyvalue, line[1024];
int i;
gm_debug_msg(DEBUG_DEFAULT, "GmOptions.load: loading options (%s)!", filename);
if ((f = fopen(filename, "r")) != NULL) {
i = 0;
while (fgets((char *) &line, 1024 - 1, f) != NULL) {
line[strlen((char *) &line) - 1] = '\0';
i++;
if (strlen(line) != 0) { // Empty lines, we don't need to process those
keyvalue = g_strsplit(line, "=", 2);
// This will return at least 1 element, at most 2, we need 2
if (strncmp(keyvalue[0], "#", 1) != 0) { // Commented lines, well ignore them too
if (keyvalue[1] != NULL) {
gm_debug_msg(DEBUG_DEFAULT, "GmOptions.load: adding %s, %s", keyvalue[0], keyvalue[1]);
gm_options_set(options, keyvalue[0], keyvalue[1]);
} else {
gm_debug_msg(DEBUG_DEFAULT, "GmOptions.load: wrong syntax of options "
"line in %s line %d", filename, i);
}
}
g_strfreev(keyvalue);
}
}
fclose(f);
} else {
gm_debug_msg(DEBUG_DEFAULT, "GmOptions.load: could not retrieve contents of file "
"%s (%s)", filename, strerror(errno));
}
g_free(options->priv->filepath);
options->priv->filepath = g_strdup(filename);
}

69
gnoemoe/gm-options.h Normal file
View File

@ -0,0 +1,69 @@
#ifndef __GM_OPTIONS_H__
#define __GM_OPTIONS_H__
#include <glib.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_OPTIONS (gm_options_get_type())
#define GM_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
GM_TYPE_OPTIONS, GmOptions))
#define GM_OPTIONS_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
GM_TYPE_OPTIONS, GmOptions const))
#define GM_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
GM_TYPE_OPTIONS, GmOptionsClass))
#define GM_IS_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
GM_TYPE_OPTIONS))
#define GM_IS_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
GM_TYPE_OPTIONS))
#define GM_OPTIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
GM_TYPE_OPTIONS, GmOptionsClass))
/* Private structure type */
typedef struct _GmOptionsPrivate GmOptionsPrivate;
/*
* Main object structure
*/
typedef struct _GmOptions GmOptions;
struct _GmOptions {
GObject object;
/*< private > */
GmOptionsPrivate *priv;
};
/*
* Class definition
*/
typedef struct _GmOptionsClass GmOptionsClass;
struct _GmOptionsClass {
GObjectClass parent_class;
/* Signals */
void (* option_changed) (GmOptions *options, const gchar *key);
};
GType gm_options_get_type(void) G_GNUC_CONST;
GmOptions *gm_options_new(void);
GmOptions *gm_options_dup(GmOptions *source);
void gm_options_add(GmOptions *options, const gchar *key, const gchar *value);
void gm_options_set(GmOptions *options, const gchar *key, const gchar *value);
const gchar *gm_options_get(GmOptions *options, const gchar *key);
void gm_options_set_int(GmOptions *options, const gchar *key, int value);
int gm_options_get_int(GmOptions *options, const gchar *key);
void gm_options_save(GmOptions *options);
void gm_options_save_as(GmOptions *options, const gchar *filename);
void gm_options_load(GmOptions *options, const gchar *filename);
G_END_DECLS
#endif /* __GM_OPTIONS_H__ */

182
gnoemoe/gm-pixbuf.c Normal file
View File

@ -0,0 +1,182 @@
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#include <gtk/gtk.h>
#include <glib.h>
#include <string.h>
#include <strings.h>
#include "gm-debug.h"
#include "gm-pixbuf.h"
static GList *gm_pixbuf_directories = NULL;
static GList *gm_pixbufs = NULL;
GdkPixbuf *gm_pixbuf_create(const gchar * filename, int width, int height);
void
gm_pixbuf_add_directory(const gchar *directory) {
gm_pixbuf_directories =
g_list_prepend(gm_pixbuf_directories, g_strdup(directory));
}
void
gm_pixbuf_init() {
gm_pixbuf_add_directory(PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps");
}
void
gm_pixbuf_fini() {
GList *l;
GmPixbufInfo *i;
for (l = gm_pixbuf_directories; l; l = l->next) {
g_free(l->data);
}
g_list_free(gm_pixbuf_directories);
for (l = gm_pixbufs; l; l = l->next) {
i = (GmPixbufInfo *)(l->data);
g_free(i->name);
g_object_unref(i->pixbuf);
g_free(i);
}
g_list_free(gm_pixbufs);
}
gchar *
gm_pixbuf_find(const gchar *filename) {
GList *elem;
if (g_file_test(filename, G_FILE_TEST_EXISTS)) {
return g_strdup(filename);
}
for (elem = gm_pixbuf_directories; elem; elem = elem->next) {
gchar *pathname =
g_strdup_printf("%s%s%s", (gchar *) elem->data,
G_DIR_SEPARATOR_S, filename);
if (g_file_test(pathname, G_FILE_TEST_EXISTS)) {
return pathname;
}
g_free(pathname);
}
return NULL;
}
GdkPixbuf *
gm_pixbuf_create(const gchar * filename, int width, int height) {
gchar *pathname = NULL, *ext;
GdkPixbuf *pixbuf = NULL;
GError *error = NULL;
if (!filename || strlen(filename) == 0) {
return NULL;
}
pathname = gm_pixbuf_find(filename);
if (!pathname) {
gm_debug_msg(DEBUG_DEFAULT, "gm_pixbuf_create: couldn't find pixbuf file: %s", filename);
return NULL;
}
ext = rindex(pathname, '.');
if (width < 1 || height < 1) {
pixbuf = gdk_pixbuf_new_from_file(pathname, &error);
} else {
pixbuf = gdk_pixbuf_new_from_file_at_size(pathname, width, height, &error);
}
if (!pixbuf) {
gm_debug_msg(DEBUG_DEFAULT, "gm_pixbuf_create: failed to load pixbuf from file: %s: %s\n",
pathname, error->message);
g_error_free(error);
error = NULL;
}
g_free(pathname);
return pixbuf;
}
GdkPixbuf *
gm_pixbuf_get_at_size(const gchar *filename, int width, int height) {
GdkPixbuf *pix;
GList *elem;
GmPixbufInfo *i;
for (elem = gm_pixbufs; elem; elem = elem->next) {
i = (GmPixbufInfo *) (elem->data);
if (strcmp(i->name, filename) == 0 &&
i->width == width && i->height == height) {
return i->pixbuf;
}
}
// Not found, so create it
pix = gm_pixbuf_create(filename, width, height);
if (pix) {
i = g_new(GmPixbufInfo, 1);
i->name = g_strdup(filename);
i->width = width;
i->height = height;
i->pixbuf = pix;
gm_pixbufs = g_list_append(gm_pixbufs, i);
return pix;
} else {
return NULL;
}
}
// Returns pixbuf if it is already loaded in pixmaps
GdkPixbuf *
gm_pixbuf_get(const gchar *filename) {
return gm_pixbuf_get_at_size(filename, -1, -1);
}
void gm_pixbuf_set_alpha(GdkPixbuf **pixs, guchar alpha) {
int width, height, n_channels, rowstride, y, x;
GdkPixbuf *pix;
guchar *pixels, *p;
if (!gdk_pixbuf_get_has_alpha(*pixs)) {
pix = gdk_pixbuf_add_alpha(*pixs, FALSE, 0, 0, 0);
gdk_pixbuf_unref(*pixs);
} else {
pix = *pixs;
}
n_channels = gdk_pixbuf_get_n_channels(pix);
if (gdk_pixbuf_get_colorspace(pix) != GDK_COLORSPACE_RGB ||
gdk_pixbuf_get_bits_per_sample(pix) != 8 ||
!gdk_pixbuf_get_has_alpha(pix) ||
n_channels != 4) {
*pixs = pix;
return;
}
width = gdk_pixbuf_get_width(pix);
height = gdk_pixbuf_get_height(pix);
rowstride = gdk_pixbuf_get_rowstride(pix);
pixels = gdk_pixbuf_get_pixels(pix);
p = pixels;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
p[3] = alpha;
p = p + n_channels;
}
}
*pixs = pix;
}

24
gnoemoe/gm-pixbuf.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef gm_pixbuf_H
#define gm_pixbuf_H 1
#include <gtk/gtk.h>
#include <glib.h>
typedef struct _GmPixbufInfo {
gchar *name;
int width;
int height;
GdkPixbuf *pixbuf;
} GmPixbufInfo;
void gm_pixbuf_init();
void gm_pixbuf_fini();
void gm_pixbuf_add_directory(const gchar *directory);
GdkPixbuf *gm_pixbuf_get(const gchar *filename);
GdkPixbuf *gm_pixbuf_get_at_size(const gchar *filename, int width, int height);
void gm_pixbuf_set_alpha(GdkPixbuf **pixs, guchar alpha);
#endif

870
gnoemoe/gm-scripts.c Normal file
View File

@ -0,0 +1,870 @@
#include <glib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <gtk/gtk.h>
#include <ruby.h>
#include <libgnomevfs/gnome-vfs-mime-utils.h>
#include <libgnomevfs/gnome-vfs.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "gm-debug.h"
#include "gm-world.h"
#include "gm-app.h"
#include "gm-scripts.h"
#include "gm-support.h"
#include "gm-world.h"
#define GM_SCRIPTS_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), \
GM_TYPE_SCRIPTS, GmScriptsPrivate))
#define RB_CALLBACK(x) (VALUE (*)())(x)
#define GM_SCRIPTS_GLOBAL PACKAGE_DATA_DIR "/" PACKAGE "/scripts"
static VALUE rb_world_class, rb_client_class, rb_scripts_class;
VALUE script_world_name(VALUE self);
VALUE gm_scripts_rb_world_new(GmWorld *world);
VALUE gm_scripts_rb_scripts_new(GmScripts *scripts);
void gm_scripts_rb_world_class_init();
void gm_scripts_rb_client_class_init();
void gm_scripts_rb_scripts_class_init();
void gm_scripts_unload(GmScripts *scripts);
void gm_scripts_rb_init();
struct _GmScriptsPrivate {
GList *files;
GmScript *loading;
GList *monitors;
};
/* Signals */
enum {
SCRIPT_ADDED,
SCRIPT_CHANGED,
SCRIPT_REMOVED,
RELOAD,
MESSAGE,
ERROR,
RUN,
NUM_SIGNALS
};
static guint gm_scripts_signals[NUM_SIGNALS] = {0};
G_DEFINE_TYPE(GmScripts, gm_scripts, G_TYPE_OBJECT)
static void
gm_scripts_finalize(GObject *object) {
GmScripts *scripts = GM_SCRIPTS(object);
GList *monitors;
gm_scripts_unload(scripts);
for (monitors = scripts->priv->monitors; monitors;
monitors = monitors->next) {
gnome_vfs_monitor_cancel((GnomeVFSMonitorHandle *)(monitors->data));
}
g_list_free(scripts->priv->monitors);
scripts->priv->monitors = NULL;
G_OBJECT_CLASS(gm_scripts_parent_class)->finalize(object);
}
static void
gm_scripts_class_init(GmScriptsClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = gm_scripts_finalize;
gm_scripts_signals[SCRIPT_ADDED] =
g_signal_new("script_added",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmScriptsClass, script_added),
NULL, NULL,
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE,
1,
G_TYPE_POINTER);
gm_scripts_signals[SCRIPT_CHANGED] =
g_signal_new("script_changed",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmScriptsClass, script_changed),
NULL, NULL,
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE,
1,
G_TYPE_POINTER);
gm_scripts_signals[SCRIPT_REMOVED] =
g_signal_new("script_removed",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmScriptsClass, script_removed),
NULL, NULL,
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE,
1,
G_TYPE_POINTER);
gm_scripts_signals[RELOAD] =
g_signal_new("reload",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmScriptsClass, reload),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
gm_scripts_signals[MESSAGE] =
g_signal_new("message",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmScriptsClass, message),
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE,
1,
G_TYPE_STRING);
gm_scripts_signals[ERROR] =
g_signal_new("error",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmScriptsClass, error),
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE,
1,
G_TYPE_STRING);
gm_scripts_signals[RUN] =
g_signal_new("run",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmScriptsClass, run),
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE,
1,
G_TYPE_STRING);
g_type_class_add_private(object_class, sizeof(GmScriptsPrivate));
gm_scripts_rb_init();
}
void
gm_scripts_rb_init(GmScriptsClass *klass) {
ruby_init();
gm_scripts_rb_world_class_init();
gm_scripts_rb_client_class_init();
gm_scripts_rb_scripts_class_init();
}
static void
gm_scripts_init(GmScripts *scripts) {
scripts->priv = GM_SCRIPTS_GET_PRIVATE(scripts);
scripts->priv->monitors = NULL;
scripts->priv->files = NULL;
}
GmScripts *
gm_scripts_new() {
GmScripts *scripts = GM_SCRIPTS(g_object_new(GM_TYPE_SCRIPTS, NULL));
return scripts;
}
void gm_scripts_monitor_cb (GnomeVFSMonitorHandle *handle,
const gchar *monitor_uri, const gchar *info_uri,
GnomeVFSMonitorEventType event_type,
GmScripts *scripts) {
gchar *filename = gnome_vfs_get_local_path_from_uri(info_uri);
switch (event_type) {
case GNOME_VFS_MONITOR_EVENT_CHANGED:
gm_scripts_reload_file(scripts, filename);
break;
case GNOME_VFS_MONITOR_EVENT_DELETED:
gm_scripts_remove_file(scripts, filename);
break;
case GNOME_VFS_MONITOR_EVENT_CREATED:
gm_scripts_add_file(scripts, filename);
break;
default:
break;
}
g_free(filename);
}
void
gm_script_function_destroy(GmScriptFunction *fi) {
g_free(fi->name);
g_free(fi->fname);
g_free(fi->description);
g_free(fi);
}
void
gm_script_destroy_functions(GmScript *script) {
GList *functions;
for (functions = script->functions; functions;
functions = functions->next) {
gm_script_function_destroy((GmScriptFunction *)(functions->data));
}
g_list_free(script->functions);
script->functions = NULL;
}
void
gm_script_destroy(GmScript *script) {
gm_script_destroy_functions(script);
g_free(script->filename);
g_free(script);
}
void
gm_scripts_unload(GmScripts *scripts) {
GList *files;
for (files = scripts->priv->files; files; files = files->next) {
gm_script_destroy((GmScript *)(files->data));
}
g_list_free(scripts->priv->files);
scripts->priv->files = NULL;
}
GmScriptFunction *
gm_scripts_find(GmScripts *scripts, gchar *name) {
GList *files;
GList *functions;
GmScriptFunction *func;
GmScript *script;
for (files = scripts->priv->files; files; files = files->next) {
script = (GmScript *)(files->data);
for (functions = script->functions; functions;
functions = functions->next) {
func = (GmScriptFunction *)(functions->data);
if (strcasecmp(func->name, name) == 0) {
return func;
}
}
}
return NULL;
}
gboolean
gm_scripts_add(GmScripts *scripts, gchar *name, gchar *fname,
gchar *description) {
GmScriptFunction *func;
if (gm_scripts_find(scripts, name) == NULL) {
func = g_new(GmScriptFunction, 1);
func->script = scripts->priv->loading;
func->name = g_strdup(name);
func->fname = g_strdup(fname);;
func->description = g_strdup(description);
scripts->priv->loading->functions =
g_list_append(scripts->priv->loading->functions, func);
return TRUE;
} else {
return FALSE;
}
}
VALUE
gm_scripts_rb_register_functions_wrap(VALUE arg) {
return rb_eval_string("register_functions");
}
void
gm_scripts_rb_script_define_world(VALUE *world) {
rb_define_variable("$world", world);
}
void
gm_scripts_rb_error(GmScripts *scripts) {
int c;
VALUE lasterr;
char *err;
gchar *msg;
VALUE ary;
if(!NIL_P(ruby_errinfo)) {
lasterr = rb_gv_get("$!");
err = RSTRING(rb_obj_as_string(lasterr))->ptr;
gm_debug_msg(DEBUG_DEFAULT, "GmScripts.Error: Error while executing Ruby code: %s",
err);
msg = g_strdup_printf(_("Error in execution: %s"), err);
g_signal_emit(scripts, gm_scripts_signals[MESSAGE], 0, msg);
g_free(msg);
ary = rb_funcall(ruby_errinfo, rb_intern("backtrace"), 0);
gm_debug_msg(DEBUG_DEFAULT, "GmScripts.Error: Ruby backtrace:");
g_signal_emit(scripts, gm_scripts_signals[ERROR], 0,
_("Ruby backtrace:"));
for (c = 0; c < RARRAY(ary)->len; c++) {
gm_debug_msg(DEBUG_DEFAULT, "GmScripts.Error: \tfrom %s",
RSTRING(RARRAY(ary)->ptr[c])->ptr);
msg = g_strdup_printf(_("\tfrom %s"),
RSTRING(RARRAY(ary)->ptr[c])->ptr);
g_signal_emit(scripts, gm_scripts_signals[ERROR], 0, msg);
g_free(msg);
}
}
}
int
gm_scripts_rb_do(GmScripts *scripts, VALUE (*body)(), VALUE arg) {
int status;
rb_protect(body, arg, &status);
if (status != 0) {
gm_scripts_rb_error(scripts);
ruby_cleanup(status);
return 0;
}
return 1;
}
VALUE
gm_scripts_run_function(GmScriptInfo *arg) {
VALUE ret;
gchar *argstr;
gchar *funcAll;
if (arg->argstr) {
argstr = gm_str_escape(arg->argstr);
funcAll = g_strconcat(arg->name, "(\"", argstr, "\")", NULL);
g_free(argstr);
} else {
funcAll = g_strconcat(arg->name, "()", NULL);
}
ret = rb_eval_string(funcAll);
g_free(funcAll);
return ret;
}
gboolean
gm_scripts_run(GmScripts *scripts, GmWorld *world, gchar *name, gchar *argstr) {
VALUE rbWorld, rbClient;
gchar *msg;
GmScriptInfo *info;
GmScriptFunction *f = gm_scripts_find(scripts, name);
if (!f) {
return FALSE;
}
info = g_new0(GmScriptInfo, 1);
info->name = g_strdup(f->fname);
if (argstr) {
info->argstr = g_strdup(argstr);
msg = g_strdup_printf(_("Run script '%s' from '%s' (%s)"), f->fname,
f->script->filename, argstr);
} else {
info->argstr = NULL;
msg = g_strdup_printf(_("Run script '%s' from '%s' ()"), f->fname,
f->script->filename);
}
g_signal_emit(scripts, gm_scripts_signals[RUN], 0, msg);
g_free(msg);
gm_scripts_rb_do(scripts, RB_CALLBACK(rb_load_file),
(VALUE)(f->script->filename));
ruby_exec();
rbWorld = gm_scripts_rb_world_new(world);
rb_define_variable("$world", &rbWorld);
rbClient = rb_class_new_instance(0, NULL, rb_client_class);
rb_define_variable("$client", &rbClient);
gm_scripts_rb_do(scripts, RB_CALLBACK(gm_scripts_run_function),
(VALUE)info);
g_free(info->name);
g_free(info->argstr);
g_free(info);
return TRUE;
}
VALUE
gm_scripts_rb_register_func_old(int argc, VALUE *argv) {
gm_debug_msg(DEBUG_DEFAULT, "GmScripts.RegisterFunc: This is the deprecated way to "
"register functions is does no longer work. Use $scripts.register "
"instead.");
return Qfalse;
}
void
gm_scripts_register_functions(GmScripts *scripts) {
gchar *msg;
VALUE rbScripts;
gm_debug_msg(DEBUG_DEFAULT, "GmScripts.RegisterFunctions: registering functions in %s",
scripts->priv->loading->filename);
msg = g_strdup_printf(_("Registering functions from '%s'"),
scripts->priv->loading->filename);
g_signal_emit(scripts, gm_scripts_signals[MESSAGE], 0, msg);
g_free(msg);
// Okay, I'm desperate... define an empty register_functions method so
// that the previous when gets cleared ... :(
rb_eval_string("def register_functions() end");
if (!gm_scripts_rb_do(scripts, RB_CALLBACK(rb_load_file),
(VALUE) scripts->priv->loading->filename)) {
return;
}
ruby_exec();
rbScripts = gm_scripts_rb_scripts_new(scripts);
rb_define_variable("$scripts", &rbScripts);
rb_define_global_function("register_func", &gm_scripts_rb_register_func_old,
-1);
gm_scripts_rb_do(scripts,
RB_CALLBACK(gm_scripts_rb_register_functions_wrap), 0);
}
void
gm_scripts_remove_file(GmScripts *scripts, const gchar *uri) {
GList *f, *l;
GmScript *script;
l = g_list_copy(scripts->priv->files);
for (f = l; f; f = f->next) {
script = (GmScript *)(f->data);
if (strcmp(script->filename, uri) == 0) {
scripts->priv->files = g_list_remove(scripts->priv->files, script);
gm_debug_msg(DEBUG_DEFAULT, "GmScripts.RemoveFile: Removing scripts from `%s'",
script->filename);
g_signal_emit(scripts, gm_scripts_signals[SCRIPT_REMOVED], 0,
script);
gm_script_destroy(script);
}
}
g_list_free(l);
}
gboolean
gm_scripts_add_file(GmScripts *scripts, const gchar *uri) {
GList *f;
GmScript *script;
gchar *msg;
for (f = scripts->priv->files; f; f = f->next) {
script = (GmScript *)(f->data);
if (strcmp(script->filename, uri) == 0) {
msg = g_strdup_printf(_("File `%s' already loaded"), uri);
gm_debug_msg(DEBUG_DEFAULT, "GmScripts.AddFile: %s", msg);
g_signal_emit(scripts, gm_scripts_signals[ERROR], 0, msg);
g_free(msg);
return FALSE;
}
}
msg = g_strdup_printf(_("File `%s' added"), uri);
gm_debug_msg(DEBUG_DEFAULT, "GmScripts.AddFile: %s", msg);
g_free(msg);
script = g_new0(GmScript, 1);
script->filename = g_strdup(uri);
if (strncmp(uri, GM_SCRIPTS_GLOBAL,
strlen(GM_SCRIPTS_GLOBAL)) == 0) {
script->type = GM_SCRIPT_TYPE_SHARE;
} else {
script->type = GM_SCRIPT_TYPE_USER;
}
scripts->priv->files = g_list_append(scripts->priv->files, script);
scripts->priv->loading = script;
gm_scripts_register_functions(scripts);
g_signal_emit(scripts, gm_scripts_signals[SCRIPT_ADDED], 0, script);
return TRUE;
}
void
gm_scripts_reload_file(GmScripts *scripts, const gchar *uri) {
GList *files;
GmScript *script;
for (files = scripts->priv->files; files; files = files->next) {
script = (GmScript *)(files->data);
if (strcmp(script->filename, uri) == 0) {
// Remove all functions and reregister the file
scripts->priv->loading = script;
gm_script_destroy_functions(script);
gm_scripts_register_functions(scripts);
g_signal_emit(scripts, gm_scripts_signals[SCRIPT_CHANGED], 0,
script);
return;
}
}
// If the script does not yet exist, add it
gm_scripts_add_file(scripts, uri);
}
void
gm_scripts_load_dir(GmScripts *scripts, gchar *dirname) {
gchar *filename;
gchar *file;
GDir *d;
GnomeVFSMonitorHandle *handle;
if (g_file_test(dirname, G_FILE_TEST_EXISTS) &&
g_file_test(dirname, G_FILE_TEST_IS_DIR)) {
if ((d = g_dir_open(dirname, 0, NULL))) {
while ((file = (gchar *)g_dir_read_name(d))) {
filename = g_strconcat(dirname, "/", file, NULL);
gm_scripts_add_file(scripts, filename);
g_free(filename);
}
}
gnome_vfs_monitor_add(&handle, dirname, GNOME_VFS_MONITOR_DIRECTORY,
(GnomeVFSMonitorCallback)gm_scripts_monitor_cb, scripts);
scripts->priv->monitors = g_list_append(scripts->priv->monitors,
handle);
}
}
void
gm_scripts_load(GmScripts *scripts) {
gchar *path;
if (scripts->priv->files) {
g_signal_emit(scripts, gm_scripts_signals[RELOAD], 0);
gm_scripts_unload(scripts);
}
path = g_strconcat(gm_app_path(gm_app_instance()), "/scripts", NULL);
gm_scripts_load_dir(scripts, path);
gm_scripts_load_dir(scripts, GM_SCRIPTS_GLOBAL);
}
GList *
gm_scripts_scripts(GmScripts *scripts) {
return scripts->priv->files;
}
// Ruby class functions
VALUE
gm_scripts_rb_world_new(GmWorld *world) {
VALUE tdata = Data_Wrap_Struct(rb_world_class, 0, 0, world);
return tdata;
}
VALUE
gm_scripts_rb_scripts_new(GmScripts *scripts) {
VALUE tdata = Data_Wrap_Struct(rb_scripts_class, 0, 0, scripts);
return tdata;
}
// Ruby world class functions
static VALUE
gm_scripts_rb_world_name(VALUE self) {
GmWorld *world;
Data_Get_Struct(self, GmWorld, world);
return rb_str_new2(gm_options_get(gm_world_options(world), "name"));
}
static VALUE
gm_scripts_rb_world_host(VALUE self) {
GmWorld *world;
Data_Get_Struct(self, GmWorld, world);
return rb_str_new2(gm_options_get(gm_world_options(world), "host"));
}
static VALUE
gm_scripts_rb_world_port(VALUE self) {
GmWorld *world;
Data_Get_Struct(self, GmWorld, world);
return rb_str_new2(gm_options_get(gm_world_options(world), "port"));
}
static VALUE
gm_scripts_rb_world_writeln(VALUE self, VALUE str) {
GmWorld *world;
gchar *strVal;
Data_Get_Struct(self, GmWorld, world);
strVal = rb_string_value_cstr(&str);
gm_world_writeln(world, strVal);
return Qnil;
}
static VALUE
gm_scripts_rb_world_sendln(VALUE self, VALUE str) {
GmWorld *world;
gchar *strVal;
Data_Get_Struct(self, GmWorld, world);
strVal = rb_string_value_cstr(&str);
gm_world_sendln(world, strVal);
return Qnil;
}
static VALUE
gm_scripts_rb_world_input(VALUE self, VALUE str) {
GmWorld *world;
gchar *strVal;
Data_Get_Struct(self, GmWorld, world);
strVal = rb_string_value_cstr(&str);
gm_world_process_input(world, strVal);
return Qnil;
}
static VALUE
gm_scripts_rb_world_loaded(VALUE self) {
GmWorld *world;
Data_Get_Struct(self, GmWorld, world);
if (gm_world_loaded(world)) {
return Qtrue;
} else {
return Qfalse;
}
}
static VALUE
gm_scripts_rb_world_connected(VALUE self) {
GmWorld *world;
Data_Get_Struct(self, GmWorld, world);
if (gm_world_state(world) == GM_NET_STATE_CONNECTED) {
return Qtrue;
} else {
return Qfalse;
}
}
static VALUE
gm_scripts_rb_world_quit(VALUE self) {
GmWorld *world;
Data_Get_Struct(self, GmWorld, world);
gm_world_unload(world);
return Qnil;
}
static VALUE
gm_scripts_rb_world_connect(int argc, VALUE *argv, VALUE self) {
GmWorld *world;
const gchar *strHost, *strPort;
Data_Get_Struct(self, GmWorld, world);
if (argc == 0) {
strHost = gm_options_get(gm_world_options(world), "host");
} else {
strHost = rb_string_value_cstr(&(argv[0]));
}
if (argc == 0 || argc == 1) {
strPort = gm_options_get(gm_world_options(world), "port");
} else {
strPort = rb_string_value_cstr(&(argv[1]));
}
gm_world_connect_to(world, (gchar *)strHost, (gchar *)strPort);
return Qnil;
}
static VALUE
gm_scripts_rb_world_disconnect(VALUE self) {
GmWorld *world;
Data_Get_Struct(self, GmWorld, world);
gm_world_disconnect(world);
return Qnil;
}
// Ruby client class functions
static VALUE
gm_scripts_rb_client_version(VALUE self) {
return rb_str_new2(VERSION);
}
static VALUE
gm_scripts_rb_client_worlds(VALUE self) {
GList *world;
VALUE rb_array = rb_ary_new();
VALUE rb_world;
for (world = gm_app_worlds(gm_app_instance()); world; world = world->next) {
rb_world = gm_scripts_rb_world_new((GmWorld *)(world->data));
rb_ary_push(rb_array, rb_world);
}
return rb_array;
}
static VALUE
gm_scripts_rb_client_open(VALUE self, VALUE str) {
GmWorld *world;
gchar *strVal;
strVal = rb_string_value_cstr(&str);
world = gm_app_world_by_name(gm_app_instance(), strVal);
if (world == NULL) {
return Qfalse;
} else {
gm_world_load(world);
return Qtrue;
}
}
// Ruby scripts class functions
VALUE
gm_scripts_rb_scripts_register(int argc, VALUE *argv, VALUE self) {
char *name, *fname = NULL, *description = NULL;
gchar *msg;
GmScripts *scripts;
Data_Get_Struct(self, GmScripts, scripts);
if (argc > 1) {
name = rb_string_value_cstr(&argv[0]);
description = rb_string_value_cstr(&argv[1]);
if (argc == 2) {
fname = name;
} else {
fname = rb_string_value_cstr(&argv[2]);
}
if (gm_scripts_add(scripts, name, fname, description)) {
msg = g_strdup_printf(_("Register function '%s' from '%s'"), name,
scripts->priv->loading->filename);
gm_debug_msg(DEBUG_DEFAULT, "GmScripts.RbScriptsRegister: Adding script function "
"%s from %s", name, scripts->priv->loading->filename);
g_signal_emit(scripts, gm_scripts_signals[MESSAGE], 0, msg);
g_free(msg);
return Qtrue;
} else {
msg = g_strdup_printf(_("Script '%s' is already defined"), name);
gm_debug_msg(DEBUG_DEFAULT, "GmScripts.RbScriptsRegister: Script function %s "
"already defined!", name);
g_signal_emit(scripts, gm_scripts_signals[ERROR], 0, msg);
g_free(msg);
return Qfalse;
}
} else {
return Qfalse;
}
}
// Ruby class initializations
void
gm_scripts_rb_world_class_init() {
rb_world_class = rb_define_class("World", rb_cObject);
rb_define_method(rb_world_class, "name", gm_scripts_rb_world_name, 0);
rb_define_method(rb_world_class, "host", gm_scripts_rb_world_host, 0);
rb_define_method(rb_world_class, "port", gm_scripts_rb_world_port, 0);
rb_define_method(rb_world_class, "writeln", gm_scripts_rb_world_writeln, 1);
rb_define_method(rb_world_class, "println", gm_scripts_rb_world_writeln, 1);
rb_define_method(rb_world_class, "sendln", gm_scripts_rb_world_sendln, 1);
rb_define_method(rb_world_class, "input", gm_scripts_rb_world_input, 1);
rb_define_method(rb_world_class, "quit", gm_scripts_rb_world_quit, 0);
rb_define_method(rb_world_class, "connect",
gm_scripts_rb_world_connect, -1);
rb_define_method(rb_world_class, "disconnect",
gm_scripts_rb_world_disconnect, 0);
rb_define_method(rb_world_class, "loaded?", gm_scripts_rb_world_loaded, 0);
rb_define_method(rb_world_class, "connected?",
gm_scripts_rb_world_connected, 0);
}
void
gm_scripts_rb_client_class_init() {
rb_client_class = rb_define_class("Client", rb_cObject);
rb_define_method(rb_client_class, "version",
gm_scripts_rb_client_version, 0);
rb_define_method(rb_client_class, "worlds",
gm_scripts_rb_client_worlds, 0);
rb_define_method(rb_client_class, "open", gm_scripts_rb_client_open, 1);
}
void
gm_scripts_rb_scripts_class_init() {
rb_scripts_class = rb_define_class("Scripts", rb_cObject);
rb_define_method(rb_scripts_class, "register",
gm_scripts_rb_scripts_register, -1);
}

109
gnoemoe/gm-scripts.h Normal file
View File

@ -0,0 +1,109 @@
#ifndef __GM_SCRIPTS_H__
#define __GM_SCRIPTS_H__
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#ifdef HAVE_RUBY
#include <glib.h>
#include <gtk/gtk.h>
#include "gm-world.h"
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_SCRIPTS (gm_scripts_get_type())
#define GM_SCRIPTS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
GM_TYPE_SCRIPTS, GmScripts))
#define GM_SCRIPTS_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
GM_TYPE_SCRIPTS, GmScripts const))
#define GM_SCRIPTS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
GM_TYPE_SCRIPTS, GmScriptsClass))
#define GM_IS_SCRIPTS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
GM_TYPE_SCRIPTS))
#define GM_IS_SCRIPTS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
GM_TYPE_SCRIPTS))
#define GM_SCRIPTS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
GM_TYPE_SCRIPTS, GmScriptsClass))
/* Private structure type */
typedef struct _GmScriptsPrivate GmScriptsPrivate;
/*
* Main object structure
*/
typedef struct _GmScripts GmScripts;
struct _GmScripts {
GObject object;
/*< private > */
GmScriptsPrivate *priv;
};
typedef enum _GmScriptType {
GM_SCRIPT_TYPE_USER,
GM_SCRIPT_TYPE_SHARE
} GmScriptType;
typedef struct _GmScript {
gchar *filename;
GmScriptType type;
GList *functions;
} GmScript;
typedef struct _GmScriptFunction {
GmScript *script;
gchar *name;
gchar *fname;
gchar *description;
} GmScriptFunction;
typedef struct _GmScriptInfo {
gchar *name;
gchar *argstr;
} GmScriptInfo;
/*
* Class definition
*/
typedef struct _GmScriptsClass GmScriptsClass;
struct _GmScriptsClass {
GObjectClass parent_class;
/* Signals */
void (* script_added) (GmScripts *scripts, GmScript *script);
void (* script_changed) (GmScripts *scripts, GmScript *script);
void (* script_removed) (GmScripts *scripts, GmScript *script);
void (* reload) (GmScripts *scripts);
void (* message) (GmScripts *scripts, gchar *message);
void (* error) (GmScripts *scripts, gchar *message);
void (* run) (GmScripts *scripts, gchar *message);
};
GType gm_scripts_get_type(void) G_GNUC_CONST;
GmScripts *gm_scripts_new();
//void gm_script_init();
//void gm_script_fini();
void gm_scripts_reload_file(GmScripts *scripts, const gchar *uri);
gboolean gm_scripts_add_file(GmScripts *scripts, const gchar *uri);
void gm_scripts_remove_file(GmScripts *scripts, const gchar *uri);
GmScriptFunction *gm_scripts_find(GmScripts *scripts, gchar *name);
GList *gm_scripts_scripts(GmScripts *scripts);
gboolean gm_scripts_run(GmScripts *scripts, GmWorld *world, gchar *name,
gchar *argstr);
void gm_scripts_load(GmScripts *scripts);
G_END_DECLS
#endif /* HAVE_RUBY */
#endif /* __GM_SCRIPTS_H__ */

203
gnoemoe/gm-string.c Normal file
View File

@ -0,0 +1,203 @@
#include <string.h>
#include <stdlib.h>
#include <glib.h>
#include "gm-string.h"
#include "gm-debug.h"
/*
void
stringlist_add(stringlist * strl, char *data) {
stringlist_item *newStringlistItem =
(stringlist_item *) malloc(sizeof(stringlist_item));
newStringlistItem->data = strdup(data);
if (strl->firstItem == NULL) {
strl->firstItem = newStringlistItem;
} else {
newStringlistItem->prev = strl->lastItem;
if (strl->lastItem != NULL)
strl->lastItem->next = newStringlistItem;
}
strl->lastItem = newStringlistItem;
newStringlistItem->next = NULL;
strl->count++;
}
void
stringlist_remove(stringlist * strl, stringlist_item * removed) {
if (removed == strl->firstItem) {
strl->firstItem = removed->next;
} else {
removed->prev->next = removed->next;
}
if (removed == strl->lastItem) {
strl->lastItem = removed->prev;
} else {
removed->next->prev = removed->next;
}
free(removed->data);
free(removed);
strl->count--;
}
stringlist *
stringlist_create(char *argstr, char *delim) {
stringlist *newStringlist = (stringlist *) malloc(sizeof(stringlist));
char *data, *work, *working, *strPos;
newStringlist->firstItem = NULL;
newStringlist->lastItem = NULL;
newStringlist->count = 0;
if (delim == NULL) {
stringlist_add(newStringlist, argstr);
return newStringlist;
}
working = strdup(argstr);
working = mystring_cat(working, delim);
work = working;
while (strlen(work) != 0) {
if ((strPos = strstr(work, delim)) != NULL && strPos > work) {
data = NULL;
data = mystring_catn(data, work, (strPos - work));
stringlist_add(newStringlist, data);
free(data);
}
work = strPos + strlen(delim);
}
free(working);
return newStringlist;
}
void
stringlist_destroy(stringlist * strl) {
stringlist_item *curItem = strl->firstItem;
while (curItem != NULL) {
stringlist_remove(strl, curItem);
curItem = strl->firstItem;
}
free(strl);
}
char *
stringlist_glue_it(stringlist * strl, char *glue) {
char *result = NULL;
stringlist_item *curItem = strl->firstItem;
while (curItem != NULL) {
if (curItem != strl->firstItem) {
result = mystring_cat(result, glue);
result = mystring_cat(result, curItem->data);
} else {
result = strdup(curItem->data);
}
}
return result;
}
*/
gboolean
gm_string_to_int(const gchar *str, int *result) {
*result = 0;
if (str == NULL || *str == '\0') {
return FALSE;
}
*result = atoi(str);
return TRUE;
}
gchar *
gm_string_catn(gchar *str, gchar *add, guint n) {
guint relen;
gchar *newstr;
if (str == NULL) {
relen = 0;
} else {
relen = strlen(str);
}
if ((newstr = (gchar *) realloc(str, (relen + n + 1) * sizeof(gchar))) == NULL) {
gm_debug_msg(DEBUG_DEFAULT, "mystring_catn: REALLOC FAILED!");
return str;
} else {
if (relen == 0) {
newstr[0] = '\0';
}
strncat(newstr, add, n);
return newstr;
}
}
gchar *
gm_string_cat(gchar *str, gchar *add) {
return gm_string_catn(str, add, strlen(add));
}
gchar *
gm_string_trim(const gchar *str) {
gchar *newstr = NULL;
const gchar *r, *l;
if (str == NULL) {
return NULL;
}
r = str + strlen(str);
l = str;
while (*l == ' ' || *r == ' ') {
if (l == r) {
break;
}
if (*l == ' ') {
l++;
}
if (l == r) {
break;
}
if (*r == ' ') {
r--;
}
}
if (l == r) {
return g_strdup("");
} else {
newstr = g_strndup(l, r - l);
return newstr;
}
}
void
gm_string_remove_char(char *str, char rem) {
int i, j = 0;
for (i = 0; str[i] != '\0'; i++) {
if (str[i] != rem) {
str[j] = str[i];
j++;
}
}
str[j] = '\0';
}

36
gnoemoe/gm-string.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef MY_STRING_H
#define MY_STRING_H 1
#include <string.h>
#include <stdlib.h>
/*typedef struct _stringlist_item stringlist_item;
struct _stringlist_item {
char *data;
stringlist_item *next;
stringlist_item *prev;
};
typedef struct _stringlist stringlist;
struct _stringlist {
stringlist_item *firstItem;
stringlist_item *lastItem;
unsigned int count;
};
void stringlist_add(stringlist * strl, char *data);
void stringlist_remove(stringlist * strl, stringlist_item * removed);
stringlist *stringlist_create(char *argstr, char *delim);
void stringlist_destroy(stringlist * strl);
char *stringlist_glue_it(stringlist * strl, char *glue);
*/
int gm_string_to_int(const gchar *str, int *result);
char *gm_string_catn(char *str, char *add, unsigned int n);
char *gm_string_cat(char *str, char *add);
char *gm_string_trim(const gchar *str);
void gm_string_remove_char(char *str, char rem);
#endif

647
gnoemoe/gm-support.c Normal file
View File

@ -0,0 +1,647 @@
#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 <locale.h>
#include <gtk/gtk.h>
#include <regex.h>
#include <libgnome/gnome-url.h>
#include <libgnomevfs/gnome-vfs.h>
#include <errno.h>
#include "gm-support.h"
#include "gm-debug.h"
#include "gm-pixbuf.h"
//#include "if_main.h"
#define URL_REGEXP "(((mailto|news|telnet|nttp|file|http|sftp|ftp|https|dav|callto)://)|(www|ftp)[-A-Za-z0-9]*\\.)[-A-Za-z0-9\\.@:]+[^]''\\.}>\\) ,\\/\\\"\\!]+(:[0-9]*)?(/|/[-A-Za-z0-9_\\$\\.\\+\\!\\*\\(\\),;:@&=\\?/~\\#\\%]*[^]'\\.}>\\) ,\\\"\\!])?"
static regex_t url_regexp;
gchar *
gm_fix_decimal_point(gchar *line, int len) {
int i;
struct lconv *l = localeconv();
if (l->decimal_point[0] == '.') {
return line;
}
for (i = 0; i < len; i++) {
if (line[i] == '.') {
line[i] = l->decimal_point[0];
}
}
return line;
}
gchar *
gm_fix_decimal_point_rev(gchar *line, int len) {
int i;
struct lconv *l = localeconv();
for (i = 0; i < len; i++) {
if (line[i] == l->decimal_point[0]) {
line[i] = '.';
}
}
return line;
}
gchar *
gm_ansi_strip(gchar * s) {
int i, j = 0;
for (i = 0; s[i] != '\0'; i++) {
// Escape sequence, advance to character after 'm'
if (s[i] == '\x1B') {
while (s[i] != '\0' && s[i] != 'm') {
i++;
}
} else if (s[i] != '\x07') {
s[j] = s[i];
j++;
}
}
s[j] = '\0';
return s;
}
int
garray_length(gchar **s) {
int i = 0;
while (s[i] != NULL) {
i++;
}
return i;
}
void g_list_free_simple(GList *s) {
GList *tmp;
for (tmp = s; tmp; tmp = tmp->next) {
g_free(tmp->data);
}
g_list_free(s);
}
gchar *
g_list_find_simple(GList *s, gchar *f) {
GList *tmp;
for (tmp = s; tmp; tmp = tmp->next) {
if (strcmp(tmp->data, f) == 0) {
return tmp->data;
}
}
return NULL;
}
void
gm_dialog(gchar * message, GtkMessageType messagebox_type,
GtkWindow * parent) {
GtkWidget *dlg;
if (parent == NULL) {
//parent = GTK_WINDOW(if_main_get_widget("wndMain"));
}
dlg =
gtk_message_dialog_new(parent,
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
messagebox_type, GTK_BUTTONS_OK, message, NULL);
gtk_dialog_run(GTK_DIALOG(dlg));
gtk_widget_destroy(dlg);
}
void
gm_error_dialog(gchar * message, GtkWindow * parent) {
gm_dialog(message, GTK_MESSAGE_ERROR, parent);
}
void
gm_warning_dialog(gchar * message, GtkWindow * parent) {
gm_dialog(message, GTK_MESSAGE_WARNING, parent);
}
void
gm_info_dialog(gchar * message, GtkWindow * parent) {
gm_dialog(message, GTK_MESSAGE_INFO, parent);
}
void
gm_question_dialog(gchar * message, GtkWindow * parent) {
gm_dialog(message, GTK_MESSAGE_QUESTION, parent);
}
void
gm_do_events() {
while (gtk_events_pending()) {
gtk_main_iteration();
}
}
gchar *
gm_str_escape(gchar * line) {
gchar *newLine;
int i, j = 0;
if (strlen(line) == 0) {
return g_strdup("");
}
if (strstr(line, "\"") || strstr(line, "\\")) {
newLine = g_new(gchar, (strlen(line) * 2) + 1);
for (i = 0; i < (int)strlen(line); i++) {
if (line[i] == '"' || line[i] == '\\') {
newLine[j] = '\\';
j++;
}
newLine[j] = line[i];
j++;
}
newLine[j] = '\0';
return newLine;
} else {
return g_strdup(line);
}
}
void
gm_directory_remove_all(const gchar * path, gboolean remove_self) {
GDir *cDir;
gchar *name;
gchar *newPath;
if (g_file_test(path, G_FILE_TEST_IS_DIR)) {
// Iterate through the files and do the right thingie
if ((cDir = g_dir_open(path, 0, NULL))) {
while ((name = (gchar *) (g_dir_read_name(cDir))) != NULL) {
newPath = g_strconcat(path, "/", name, NULL);
gm_directory_remove_all(newPath, TRUE);
g_free(newPath);
}
g_dir_close(cDir);
}
}
if (remove_self) {
// Its a file, or just empty! MUST...BE...REMOVEEEED!
remove(path);
}
}
gint
gm_url_regex_match(const gchar *msg, int len, GArray *start, GArray *end) {
static gboolean inited = FALSE;
regmatch_t matches[1];
gint ret = 0, num_matches = 0, offset = 0;
gchar *tmp;
gint s;
if (!inited) {
memset(&url_regexp, 0, sizeof (regex_t));
regcomp(&url_regexp, URL_REGEXP, REG_EXTENDED);
inited = TRUE;
}
tmp = g_strndup(msg, len);
while (!ret) {
ret = regexec(&url_regexp, (char *)(tmp + offset), 1, matches, 0);
if (ret == 0) {
if (matches[0].rm_so > matches[0].rm_eo) {
break;
}
num_matches++;
s = matches[0].rm_so + offset;
offset = matches[0].rm_eo + offset;
g_array_append_val(start, s);
g_array_append_val(end, offset);
}
}
g_free(tmp);
return num_matches;
}
void
gm_open_url (const gchar *url) {
if (!url || strlen (url) == 0) {
return;
}
/* gnome_url_show doesn't work when there's no protocol, so we might
* need to add one.
*/
if (strstr (url, "://") == NULL) {
gchar *tmp;
tmp = g_strconcat ("http://", url, NULL);
gnome_url_show(tmp, NULL);
g_free (tmp);
return;
}
gnome_url_show (url, NULL);
}
void
gm_fetch_handle_free(GmFetchHandle *g) {
GList *tmp;
g_free(g->cur_file_name);
for (tmp = g->source_uri; tmp; tmp = tmp->next) {
gnome_vfs_uri_unref((GnomeVFSURI*)(tmp->data));
}
g_list_free(g->source_uri);
for (tmp = g->dest_uri; tmp; tmp = tmp->next) {
gnome_vfs_uri_unref((GnomeVFSURI*)(tmp->data));
}
g_list_free(g->dest_uri);
g_free(g);
}
GmFetchHandle *
gm_fetch_handle_create(GFunc cb, gpointer user_data) {
GmFetchHandle *g = g_new0(GmFetchHandle, 1);
g->cb = cb;
g->user_data = user_data;
g->prev_status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
g->cur_phase = -1;
g->prev_phase = -1;
g->cur_file = -1;
g->prev_file = -1;
g->source_uri = NULL;
g->dest_uri = NULL;
g->cur_file_name = NULL;
g->files_total = 0;
g->done = FALSE;
g->aborted = FALSE;
return g;
}
gint
gm_fetch_progress(GnomeVFSAsyncHandle *handle,
GnomeVFSXferProgressInfo *info,
GmFetchHandle *g) {
gchar *name;
const gchar *err;
g->cur_phase = info->phase;
g->cur_file = info->file_index;
g->files_total = info->files_total;
g->bytes_total = info->bytes_total;
g->file_size = info->file_size;
g->bytes_copied = info->bytes_copied;
g->total_bytes_copied = info->total_bytes_copied;
g->status = info->status;
if (g->aborted) {
g->cb(g, g->user_data);
gm_fetch_handle_free(g);
return FALSE;
}
if (info->target_name != NULL) {
if (g->cur_file_name && strcmp(g->cur_file_name, info->target_name) != 0) {
g->cur_phase = GNOME_VFS_XFER_PHASE_FILECOMPLETED;
g->cb(g, g->user_data);
g->cur_phase = info->phase;
g_free(g->cur_file_name);
g->cur_file_name = NULL;
}
if (!g->cur_file_name) {
g->cur_file_name = g_strdup(info->target_name);
}
}
if (info->status == GNOME_VFS_XFER_PROGRESS_STATUS_OVERWRITE) {
name = gnome_vfs_get_local_path_from_uri(info->target_name);
gm_debug_msg(DEBUG_DEFAULT, "gnoemoe_fetch_progress: asking for overwriting %s: yes", name);
g->prev_status = GNOME_VFS_XFER_PROGRESS_STATUS_OVERWRITE;
return GNOME_VFS_XFER_OVERWRITE_ACTION_REPLACE;
} else if (info->status == GNOME_VFS_XFER_PROGRESS_STATUS_VFSERROR) {
name = gnome_vfs_get_local_path_from_uri(info->target_name);
err = gnome_vfs_result_to_string(info->vfs_status);
gdk_threads_enter();
gm_debug_msg(DEBUG_DEFAULT, "gnoemoe_fetch_progress: error for %s: %s", name, err);
g->cb(g, g->user_data);
gdk_threads_leave();
g_free(g->cur_file_name);
g->cur_file_name = NULL;
g->prev_status = GNOME_VFS_XFER_PROGRESS_STATUS_VFSERROR;
g_free(name);
return GNOME_VFS_XFER_ERROR_ACTION_SKIP;
}
if (info->phase == GNOME_VFS_XFER_PHASE_COMPLETED) {
if (g->cur_file_name != NULL) {
g->cur_phase = GNOME_VFS_XFER_PHASE_FILECOMPLETED;
g->cb(g, g->user_data);
g->cur_phase = info->phase;
}
g->done = TRUE;
g->cb(g, g->user_data);
gm_fetch_handle_free(g);
return TRUE;
}
g->prev_status = info->status;
return TRUE;
}
gint
gm_fetch_interact(GnomeVFSXferProgressInfo *info, gpointer user_data) {
return 1;
}
GmFetchHandle *
gm_fetch(const GList *source, const GList *dest,
GFunc cb, gpointer user_data) {
GmFetchHandle *g = gm_fetch_handle_create(cb, user_data);
gchar *uri;
for (; source; source = source->next) {
uri = (gchar *)(source->data);
g->source_uri = g_list_append(g->source_uri, gnome_vfs_uri_new(uri));
}
for (; dest; dest = dest->next) {
uri = (gchar *)(dest->data);
g->dest_uri = g_list_append(g->dest_uri, gnome_vfs_uri_new(uri));
}
gnome_vfs_async_xfer(&(g->handle), g->source_uri, g->dest_uri,
GNOME_VFS_XFER_DEFAULT|GNOME_VFS_XFER_RECURSIVE,
GNOME_VFS_XFER_ERROR_MODE_QUERY,
GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE,
GNOME_VFS_PRIORITY_DEFAULT,
(GnomeVFSAsyncXferProgressCallback)gm_fetch_progress,
g, gm_fetch_interact, g);
return g;
}
gboolean
gm_is_end_scrolled(GtkScrolledWindow *wnd, guint charHeight) {
GtkAdjustment *ad = gtk_scrolled_window_get_vadjustment(wnd);
return ((ad->page_size + ad->value) >= ad->upper - (double)charHeight);
}
void
gm_scroll_end(GtkTextView *view, gboolean needs) {
GtkTextBuffer *buf;
GtkTextMark *mark;
GtkTextIter iter;
if (!needs) {
return;
}
buf = gtk_text_view_get_buffer(view);
mark = gtk_text_buffer_get_mark(buf, "end-of-buffer");
if (mark == NULL) {
gtk_text_buffer_get_end_iter(buf, &iter);
mark = gtk_text_buffer_create_mark(buf, "end-of-buffer", &iter, FALSE);
}
gtk_text_view_scroll_to_mark(view, mark, 0.0, TRUE, 1.0, 1.0);
}
#define MAX_BUF 1024
GString *
gm_read_file(const gchar *fname, gboolean readall, OpenLogProgress func, gpointer user_data) {
FILE *f;
gchar line[MAX_BUF], *tmp;
GString *str = NULL;
long bytes_read = 0, bytes_total = 0;
if (!fname) {
return NULL;
}
f = fopen(fname, "r");
if (f) {
fseek(f, 0, SEEK_END);
bytes_total = ftell(f);
rewind(f);
str = g_string_new("");
while (fgets((char *) &line, MAX_BUF, f) != NULL) {
bytes_read += strlen((char *)&line);
tmp = NULL;
if (g_utf8_validate(line, -1, NULL)) {
if (readall) {
str = g_string_append(str, line);
}
tmp = g_strdup(line);
} else {
tmp = g_locale_to_utf8(line, -1, NULL, NULL, NULL);
if (!tmp) {
tmp = g_convert(line, -1, "UTF-8", "ISO-8859-15", NULL, NULL, NULL);
}
if (!tmp) {
tmp = g_convert(line, -1, "UTF-8", "ISO-8859-15", NULL, NULL, NULL);
}
if (readall) {
str = g_string_append(str, tmp);
}
}
if (func != NULL) {
func(bytes_read, bytes_total, tmp, user_data);
} else {
g_free(tmp);
}
}
fclose(f);
return str;
} else {
gm_debug_msg(DEBUG_DEFAULT, "support_read_file: file (%s) could not be read: %s",
fname, strerror(errno));
return NULL;
}
}
GtkWidget *
gm_create_tab_label(const gchar *icon, const gchar *caption, gboolean has_exit,
GmLabelInfo *info) {
/* First create the gbox (size 3) which will contain an icon, a label and a
exit button if has_exit is true */
GtkWidget *hboxTabLabel;
gint h, w;
hboxTabLabel = gtk_hbox_new(FALSE, 3);
gtk_widget_show(hboxTabLabel);
info->image_icon = gtk_image_new_from_pixbuf(
gm_pixbuf_get_at_size(icon, 16, 16));
gtk_widget_set_size_request(info->image_icon, 16, 16);
gtk_widget_show(info->image_icon);
gtk_box_pack_start(GTK_BOX(hboxTabLabel), info->image_icon, TRUE, TRUE, 0);
info->label_name = gtk_label_new(caption);
gtk_widget_show(info->label_name);
gtk_box_pack_start(GTK_BOX(hboxTabLabel), info->label_name, FALSE, FALSE, 0);
if (has_exit) {
info->button_exit = gtk_button_new();
gtk_widget_show(info->button_exit);
gtk_box_pack_end(GTK_BOX(hboxTabLabel), info->button_exit , FALSE,
FALSE, 0);
gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &w, &h);
gtk_widget_set_size_request(info->button_exit , w + 2, h + 2);
gtk_button_set_relief(GTK_BUTTON(info->button_exit ), GTK_RELIEF_NONE);
gtk_button_set_focus_on_click(GTK_BUTTON(info->button_exit), FALSE);
info->image_exit = gtk_image_new_from_stock("gtk-close",
GTK_ICON_SIZE_MENU);
gtk_widget_show(info->image_exit);
gtk_container_add(GTK_CONTAINER(info->button_exit), info->image_exit);
}
return hboxTabLabel;
}
void
gm_widget_destroy_data(GtkWidget *caller, GtkWidget *destroyer) {
if (GTK_IS_WIDGET(destroyer)) {
gtk_widget_destroy(destroyer);
}
}
const gchar *
gm_default_charset() {
const gchar *loc = NULL;
g_get_charset(&loc);
if (loc == NULL || strlen(loc) == 0) {
loc = "ISO-8859-15";
}
return loc;
}
void
gm_notebook_focus_from_label(GtkNotebook *note, gchar *caption) {
int p = gtk_notebook_get_n_pages(note);
int i;
GtkWidget *child;
for (i = 0; i < p; i++) {
child = gtk_notebook_get_nth_page(note, i);
if (!g_strcasecmp(gtk_notebook_get_tab_label_text(note, child),
caption)) {
gtk_notebook_set_current_page(note, i);
break;
}
}
}
void
gm_string_skip_space(gchar **ptr) {
while (**ptr != '\0' && g_unichar_isspace(g_utf8_get_char(*ptr))) {
*ptr = g_utf8_next_char(*ptr);
}
}
void
gm_string_skip_nonspace(gchar **ptr) {
while (**ptr != '\0' && !g_unichar_isspace(g_utf8_get_char(*ptr))) {
*ptr = g_utf8_next_char(*ptr);
}
}
void
gm_string_skip_till(gchar **ptr, gchar const *find) {
gchar const *fptr;
gunichar check;
while (**ptr != '\0') {
check = g_utf8_get_char(*ptr);
for (fptr = find; *fptr; ++fptr) {
// CHECK: find should also be treated as utf8!
if (check == (gunichar)(*fptr)) {
return;
}
}
*ptr = g_utf8_next_char(*ptr);
}
}
gchar *
gm_to_utf8_with_fallback(gchar const *text, gssize len, gchar const *from,
gchar const *fallback) {
gchar *res;
gsize read, written;
GString *str = g_string_new("");
// TODO: use g_iconv instead of g_convert
while ((res = g_convert(text, len, "UTF-8", from, &read, &written, NULL))
== NULL) {
res = g_convert(text, read, "UTF-8", from, NULL, NULL, NULL);
str = g_string_append(str, res);
str = g_string_append(str, fallback);
text = text + read + 1;
if (len != -1)
len = len - read - 1;
}
str = g_string_append(str, res);
g_free(res);
res = str->str;
g_string_free(str, FALSE);
return res;
}

156
gnoemoe/gm-support.h Normal file
View File

@ -0,0 +1,156 @@
#ifndef __GM_SUPPORT_H__
#define __GM_SUPPORT_H__
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#include <gtk/gtk.h>
#include <libgnomevfs/gnome-vfs.h>
/*
* Standard gettext macros.
*/
#ifdef ENABLE_NLS
# include <libintl.h>
# undef _
# define _(String) dgettext (PACKAGE, String)
# ifdef gettext_noop
# define N_(String) gettext_noop (String)
# else
# define N_(String) (String)
# endif
#else
# define textdomain(String) (String)
# define gettext(String) (String)
# define dgettext(Domain,Message) (Message)
# define dcgettext(Domain,Message,Type) (Message)
# define bindtextdomain(Domain,Directory) (Domain)
#ifndef _
# define _(String) (String)
#endif
# define N_(String) (String)
#endif
#define CALC_COLOR_RANGE(x) (int)((-(1/((x * 0.5)+1))+1)*255)
/** \defgroup support */
/** \ingroup support
* \brief Widget container for tabs
*
* Contains all widgets that could be useful to be modified.
* Stored here because it makes it easier to look them up.
*
*/
typedef struct _GmLabelInfo GmLabelInfo;
struct _GmLabelInfo {
/** \brief GtkButton widget
*
* GtkButton widget, reference to the tabs exit button
*/
GtkWidget *button_exit;
/** \brief GtkImage widget
*
* GtkImage widget, reference to the tabs exit button image
*/
GtkWidget *image_exit;
/** \brief GtkLabel widget
*
* GtkLabel widget, reference to the tabs label
*/
GtkWidget *label_name;
/** \brief GtkImage widget
*
* GtkImage widget, reference to the tabs icon
*/
GtkWidget *image_icon;
};
/** \ingroup support
* \brief Key/value pair struct
*
* Contains two fields. Used to create hash tables
*
*/
typedef struct _GmKeyValuePair {
gchar *key; /**< the key */
gchar *value; /**< the value */
} GmKeyValuePair;
typedef struct _GmFetchHandle GmFetchHandle;
struct _GmFetchHandle {
GnomeVFSAsyncHandle *handle;
GFunc cb;
gpointer user_data;
GList *source_uri;
GList *dest_uri;
GnomeVFSXferPhase cur_phase;
GnomeVFSXferPhase prev_phase;
GnomeVFSXferProgressStatus prev_status;
GnomeVFSXferProgressStatus status;
GnomeVFSFileSize bytes_total;
GnomeVFSFileSize file_size;
GnomeVFSFileSize bytes_copied;
GnomeVFSFileSize total_bytes_copied;
gulong files_total;
gulong cur_file;
gulong prev_file;
gchar *cur_file_name;
gboolean aborted;
gboolean done;
};
gchar *gm_fix_decimal_point(gchar *line, int len);
gchar *gm_fix_decimal_point_rev(gchar *line, int len);
gchar *gm_ansi_strip(gchar * s);
int garray_length(gchar **s);
void g_list_free_simple(GList *s);
gchar *g_list_find_simple(GList *s, gchar *f);
void gm_error_dialog(gchar * message, GtkWindow * parent);
void gm_warning_dialog(gchar * message, GtkWindow * parent);
void gm_info_dialog(gchar * message, GtkWindow * parent);
void gm_question_dialog(gchar * message, GtkWindow * parent);
void gm_do_events();
gchar *gm_str_escape(gchar * line);
void gm_directory_remove_all(const gchar * path, gboolean remove_self);
gint gm_url_regex_match(const gchar *msg, int len, GArray *start, GArray *end);
void gm_open_url (const gchar *url);
GmFetchHandle * gm_fetch(const GList *source, const GList *dest,
GFunc cb, gpointer user_data);
void gm_fetch_handle_free(GmFetchHandle *g);
gboolean gm_is_end_scrolled(GtkScrolledWindow *wnd, guint charHeight);
void gm_scroll_end(GtkTextView *view, gboolean needs);
typedef void (*OpenLogProgress) (long, long, gchar *, gpointer);
GString *gm_read_file(const gchar *fname, gboolean readall,
OpenLogProgress func, gpointer user_data);
GtkWidget *gm_create_tab_label(const gchar *icon, const gchar *caption,
gboolean has_exit, GmLabelInfo *info);
void gm_widget_destroy_data(GtkWidget *caller, GtkWidget *destroyer);
const gchar *gm_default_charset();
void gm_notebook_focus_from_label(GtkNotebook *note, gchar *caption);
void gm_string_skip_space(gchar **ptr);
void gm_string_skip_nonspace(gchar **ptr);
void gm_string_skip_till(gchar **ptr, gchar const *find);
gchar *gm_to_utf8_with_fallback(gchar const *text, gssize len, gchar const *from,
gchar const *fallback);
#endif /* __GM_SUPPORT_H__ */

55
gnoemoe/gm-tray.c Normal file
View File

@ -0,0 +1,55 @@
#include "gm-tray.h"
#include "eggtrayicon.h"
#define GM_TRAY_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_TRAY, GmTrayPrivate))
typedef enum _tray_type {
TRAY_ICON_DEFAULT,
TRAY_ICON_ACTIVE,
TRAY_ICON_NOTIFY
} tray_type;
struct _GmTrayPrivate {
GtkWidget *event_box;
GtkWidget *image;
GtkTooltips *tooltips;
GtkWidget *popup_menu;
GtkWidget *show_popup_item;
GtkWidget *hide_popup_item;
guint flash_timeout;
tray_type iconnr;
};
/* Signals */
enum {
NUM_SIGNALS
};
static guint tray_signals[NUM_SIGNALS] = {0};
G_DEFINE_TYPE(GmTray, gm_tray, EGG_TYPE_TRAY_ICON)
static void
gm_tray_finalize(GObject *object) {
GmTray *view = GM_TRAY(object);
G_OBJECT_CLASS(gm_tray_parent_class)->finalize(object);
}
static void
gm_tray_class_init(GmTrayClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = gm_tray_finalize;
g_type_class_add_private(object_class, sizeof(GmTrayPrivate));
}
static void
gm_tray_init(GmTray *tray) {
tray->private = GM_TRAY_GET_PRIVATE(tray);
}

50
gnoemoe/gm-tray.h Normal file
View File

@ -0,0 +1,50 @@
#ifndef __GM_TRAY_H__
#define __GM_TRAY_H__
#include <gtk/gtk.h>
#include "eggtrayicon.h"
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_TRAY (gm_tray_get_type())
#define GM_TRAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_TRAY, GmTray))
#define GM_TRAY_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_TRAY, GmTray const))
#define GM_TRAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_TRAY, GmTrayClass))
#define GM_IS_TRAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_TRAY))
#define GM_IS_TRAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_TRAY))
#define GM_TRAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_TRAY, GmTrayClass))
/* Private structure type */
typedef struct _GmTrayPrivate GmTrayPrivate;
/*
* Main object structure
*/
typedef struct _GmTray GmTray;
struct _GmTray {
EggTrayIcon trayicon;
/*< private > */
GmTrayPrivate *priv;
};
/*
* Class definition
*/
typedef struct _GmTrayClass GmTrayClass;
struct _GmTrayClass {
EggTrayIconClass parent_class;
/* Signals */
};
GType gm_tray_get_type(void) G_GNUC_CONST;
GmTray *gm_tray_new(void);
G_END_DECLS
#endif /* __GM_TRAY_H__ */

440
gnoemoe/gm-triggers.c Normal file
View File

@ -0,0 +1,440 @@
#include <strings.h>
#include <string.h>
#include <libxml/parser.h>
#include "gm-triggers.h"
#include "gm-debug.h"
#define GM_TRIGGERS_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_TRIGGERS, GmTriggersPrivate))
typedef struct _trigger_trans {
gint type;
const gchar *name;
} trigger_trans;
static const trigger_trans table_conditions[] = {
{TCT_CONTAINS, "contains"},
{TCT_NOT_CONTAINS, "not-contains"},
{TCT_BEGINS, "begins"},
{TCT_NOT_BEGINS, "not-begins"},
{TCT_ENDS, "ends"},
{TCT_NOT_ENDS, "not-ends"},
{TCT_MATCHES, "matches"},
{TCT_NOT_MATCHES, "not-matches"},
{TCT_USER_ONLINE, "online"},
{TCT_USER_OFFLINE, "offline"},
{TCT_USER_IDLE, "idle"},
{TCT_USER_IDLE_OFF, "idle-off"},
{TCT_USER_AWAY, "away"},
{TCT_USER_AWAY_OFF, "away-off"},
{-1, NULL}
};
static const trigger_trans table_actions[] = {
{TAT_HIGHLIGHT_LINE, "highlight-line"},
{TAT_HIGHLIGHT_MATCH, "highlight-match"},
{TAT_BEEP, "beep"},
{TAT_PLAY_SOUND, "play-sound"},
{TAT_NOTIFY, "notify"},
#ifdef HASRUBY
{TAT_RUN_SCRIPT, "run-script"},
#endif
{TAT_RUN, "run"},
{-1, NULL}
};
struct _GmTriggersPrivate {
GList *triggers;
gchar *path;
};
/* Signals */
/*enum {
PROTO
NUM_SIGNALS
};
static guint triggers_signals[NUM_SIGNALS] = {0};*/
G_DEFINE_TYPE(GmTriggers, gm_triggers, G_TYPE_OBJECT)
static void
gm_triggers_finalize(GObject *object) {
GmTriggers *trg = GM_TRIGGERS(object);
GList *item;
for (item = trg->priv->triggers; item; item = item->next) {
gm_trigger_free((GmTrigger *)(item->data));
}
g_free(trg->priv->path);
g_list_free(trg->priv->triggers);
G_OBJECT_CLASS(gm_triggers_parent_class)->finalize(object);
}
static void
gm_triggers_class_init(GmTriggersClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = gm_triggers_finalize;
g_type_class_add_private(object_class, sizeof(GmTriggersPrivate));
}
static void
gm_triggers_init(GmTriggers *trg) {
trg->priv = GM_TRIGGERS_GET_PRIVATE(trg);
trg->priv->triggers = NULL;
trg->priv->path = NULL;
}
GmTrigger *
gm_trigger_new() {
return g_new0(GmTrigger, 1);
}
GList *
gm_trigger_list_dup(GList *list) {
GList *result = NULL;
GList *item;
GmTriggerData *data;
for (item = list; item; item = item->next) {
data = (GmTriggerData *)(item->data);
result = g_list_append(result, gm_trigger_data_new(data->type,
g_strdup(data->data)));
}
return result;
}
GmTrigger *
gm_trigger_dup(GmTrigger *source) {
GmTrigger *result = gm_trigger_new();
result->name = g_strdup(source->name);
result->event = source->event;
result->conditions = gm_trigger_list_dup(source->conditions);
result->actions = gm_trigger_list_dup(source->actions);
return result;
}
void
gm_trigger_set_name(GmTrigger *trigger, const gchar *name) {
g_free(trigger->name);
trigger->name = g_strdup(name);
}
void
gm_trigger_free_list(GList *list) {
GList *item;
GmTriggerData *data;
for (item = list; item; item = item->next) {
data = (GmTriggerData *)(item->data);
gm_trigger_data_free(data);
}
g_list_free(list);
}
void
gm_trigger_free(GmTrigger *trigger) {
gm_trigger_free_list(trigger->conditions);
gm_trigger_free_list(trigger->actions);
g_free(trigger->name);
g_free(trigger);
}
GmTriggerData *
gm_trigger_data_new(gint type, gchar *data) {
GmTriggerData *tdata = g_new0(GmTriggerData, 1);
tdata->type = type;
tdata->data = data;
memset(&(tdata->expr), 0, sizeof(regex_t));
switch (tdata->type) {
case TCT_MATCHES: case TCT_NOT_MATCHES: case TCT_USER_ONLINE:
case TCT_USER_OFFLINE: case TCT_USER_IDLE: case TCT_USER_IDLE_OFF:
case TCT_USER_AWAY: case TCT_USER_AWAY_OFF:
regcomp(&(tdata->expr), tdata->data, REG_EXTENDED);
break;
default:
break;
}
return tdata;
}
void
gm_trigger_data_free(GmTriggerData *tdata) {
g_free(tdata->data);
g_free(tdata);
}
void
gm_trigger_add_condition(GmTrigger *trigger, GmTriggerData *condition) {
trigger->conditions = g_list_append(trigger->conditions, condition);
}
void
gm_trigger_add_action(GmTrigger *trigger, GmTriggerData *action) {
trigger->actions = g_list_append(trigger->actions, action);
}
void
gm_trigger_set_conditions(GmTrigger *trigger, GList *conditions) {
gm_trigger_free_list(trigger->conditions);
trigger->conditions = conditions;
}
void
gm_trigger_set_actions(GmTrigger *trigger, GList *actions) {
gm_trigger_free_list(trigger->actions);
trigger->actions = actions;
}
gint
gm_trigger_type_from_name(const gchar *name,
const trigger_trans *trans_table) {
int i;
for (i = 0; trans_table[i].type != -1; i++) {
if (strcasecmp(trans_table[i].name, name) == 0) {
return trans_table[i].type;
}
}
return -1;
}
const gchar *
gm_trigger_name_from_type(gint type, const trigger_trans *trans_table) {
int i;
for (i = 0; trans_table[i].type != -1; i++) {
if (trans_table[i].type == type) {
return trans_table[i].name;
}
}
return NULL;
}
void
gm_triggers_parse_trigger(GmTriggers *trg, xmlDocPtr doc, xmlNodePtr node) {
GmTrigger *result = gm_trigger_new();
xmlChar *tmp, *tmp2;
gint type;
tmp = xmlGetProp(node, (const xmlChar *)"name");
result->name = (gchar *)tmp;
tmp = xmlGetProp(node, (const xmlChar *)"event");
if (xmlStrcmp(tmp, (const xmlChar *)("world")) == 0) {
result->event = TT_OUTPUT;
} else if (xmlStrcmp(tmp, (const xmlChar *)("player")) == 0){
result->event = TT_USERS;
} else {
xmlFree(tmp);
gm_trigger_free(result);
return;
}
xmlFree(tmp);
for (node = node->xmlChildrenNode; node; node = node->next) {
tmp = xmlGetProp(node, (const xmlChar *)"type");
tmp2 = xmlGetProp(node, (const xmlChar *)"data");
if (xmlStrcmp(node->name, (const xmlChar *)("condition")) == 0) {
type = gm_trigger_type_from_name((const gchar *)(tmp),
table_conditions);
if (type != -1) {
gm_trigger_add_condition(result, gm_trigger_data_new(type,
g_strdup((char *)tmp2)));
}
} else if (xmlStrcmp(node->name, (const xmlChar *)("action")) == 0) {
type = gm_trigger_type_from_name((const gchar *)(tmp),
table_actions);
if (type != -1) {
gm_trigger_add_action(result, gm_trigger_data_new(type,
g_strdup((char *)tmp2)));
}
}
xmlFree(tmp2);
xmlFree(tmp);
}
gm_triggers_add(trg, result);
}
GmTriggers *
gm_triggers_new() {
GmTriggers *trg = GM_TRIGGERS(g_object_new(GM_TYPE_TRIGGERS, NULL));
return trg;
}
void
gm_triggers_add(GmTriggers *trg, GmTrigger *t) {
trg->priv->triggers = g_list_append(trg->priv->triggers, t);
}
void
gm_triggers_clear(GmTriggers *trg) {
GList *item;
for (item = trg->priv->triggers; item; item = item->next) {
gm_trigger_free((GmTrigger *)(item->data));
}
g_list_free(trg->priv->triggers);
trg->priv->triggers = NULL;
}
GmTriggers *
gm_triggers_new_from_file(gchar *filename) {
GmTriggers *trg = GM_TRIGGERS(g_object_new(GM_TYPE_TRIGGERS, NULL));
xmlDocPtr doc;
xmlNodePtr cur;
if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
gm_debug_msg(DEBUG_DEFAULT, "GmTriggers.NewFromFile: Trigger file does not exist");
return trg;
}
trg->priv->path = g_strdup(filename);
doc = xmlParseFile(filename);
if (doc == NULL) {
gm_debug_msg(DEBUG_DEFAULT, "GmTriggers.NewFromFile: Error on parsing triggers");
return trg;
}
cur = xmlDocGetRootElement(doc);
if (cur == NULL) {
xmlFreeDoc(doc);
return trg;
}
if (xmlStrcmp(cur->name, (const xmlChar *)("triggers"))) {
gm_debug_msg(DEBUG_DEFAULT, "GmTriggers.NewFromFile: invalid root node");
xmlFreeDoc(doc);
return trg;
}
for (cur = cur->xmlChildrenNode; cur; cur = cur->next) {
if (!xmlStrcmp(cur->name, (const xmlChar *)("trigger"))) {
gm_triggers_parse_trigger(trg, doc, cur);
}
}
xmlFreeDoc(doc);
return trg;
}
void
gm_triggers_set_path(GmTriggers *trg, gchar *path) {
g_free(trg->priv->path);
trg->priv->path = g_strdup(path);
}
void
gm_trigger_rules_xml(GmTrigger *t, xmlNodePtr trig) {
GmTriggerData *data;
GList *item;
xmlNodePtr child;
for (item = t->conditions; item; item = item->next) {
data = (GmTriggerData *)(item->data);
child = xmlNewChild(trig, NULL, (const xmlChar *)("condition"), NULL);
xmlNewProp(child, (const xmlChar *)("type"), (const xmlChar *)
(gm_trigger_name_from_type(data->type, table_conditions)));
if (data->data) {
xmlNewProp(child, (const xmlChar *)("data"), (const xmlChar *)
(data->data));
}
}
for (item = t->actions; item; item = item->next) {
data = (GmTriggerData *)(item->data);
child = xmlNewChild(trig, NULL, (const xmlChar *)("action"), NULL);
xmlNewProp(child, (const xmlChar *)("type"), (const xmlChar *)
(gm_trigger_name_from_type(data->type, table_actions)));
if (data->data) {
xmlNewProp(child, (const xmlChar *)("data"), (const xmlChar *)
(data->data));
}
}
}
void
gm_triggers_save(GmTriggers *trg) {
xmlDocPtr doc;
xmlNodePtr root;
xmlNodePtr trig;
GList *item;
GmTrigger *t;
g_return_if_fail(trg->priv->path != NULL);
doc = xmlNewDoc((const xmlChar *)("1.0"));
root = xmlNewNode(NULL, (const xmlChar *)("triggers"));
xmlDocSetRootElement(doc, root);
for (item = trg->priv->triggers; item; item = item->next) {
t = (GmTrigger *)(item->data);
trig = xmlNewChild(root, NULL, (const xmlChar *)("trigger"), NULL);
xmlNewProp(trig, (const xmlChar *)("name"), (const xmlChar *)(t->name));
if (t->event == TT_OUTPUT) {
xmlNewProp(trig, (const xmlChar *)("event"), (const xmlChar *)("world"));
} else {
xmlNewProp(trig, (const xmlChar *)("event"), (const xmlChar *)("player"));
}
gm_trigger_rules_xml(t, trig);
}
xmlSaveFormatFileEnc(trg->priv->path, doc, "UTF-8", 1);
xmlFreeDoc(doc);
}
void
gm_triggers_save_as(GmTriggers *trg, const gchar *path) {
g_free(trg->priv->path);
trg->priv->path = g_strdup(path);
gm_triggers_save(trg);
}
GmTriggers *
gm_triggers_dup(GmTriggers *source) {
GmTriggers *trg = GM_TRIGGERS(g_object_new(GM_TYPE_TRIGGERS, NULL));
GList *item;
for (item = source->priv->triggers; item; item = item->next) {
trg->priv->triggers = g_list_append(trg->priv->triggers,
gm_trigger_dup((GmTrigger *)(item->data)));
}
return trg;
}
const GList *
gm_triggers_list(GmTriggers *trg) {
return trg->priv->triggers;
}

118
gnoemoe/gm-triggers.h Normal file
View File

@ -0,0 +1,118 @@
#ifndef __GM_TRIGGERS_H__
#define __GM_TRIGGERS_H__
#include <glib.h>
#include <glib-object.h>
#include <regex.h>
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_TRIGGERS (gm_triggers_get_type())
#define GM_TRIGGERS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_TRIGGERS, GmTriggers))
#define GM_TRIGGERS_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_TRIGGERS, GmTriggers const))
#define GM_TRIGGERS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_TRIGGERS, GmTriggersClass))
#define GM_IS_TRIGGERS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_TRIGGERS))
#define GM_IS_TRIGGERS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_TRIGGERS))
#define GM_TRIGGERS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_TRIGGERS, GmTriggersClass))
/* Private structure type */
typedef struct _GmTriggersPrivate GmTriggersPrivate;
/*
* Main object structure
*/
typedef struct _GmTriggers GmTriggers;
struct _GmTriggers {
GObject object;
/*< private > */
GmTriggersPrivate *priv;
};
/*
* Class definition
*/
typedef struct _GmTriggersClass GmTriggersClass;
struct _GmTriggersClass {
GObjectClass parent_class;
/* Signals */
};
typedef enum _GmTriggerType {
TT_OUTPUT = 0,
TT_USERS
} GmTriggerType;
typedef enum _GmTriggerConditionType {
TCT_CONTAINS = 0,
TCT_NOT_CONTAINS,
TCT_BEGINS,
TCT_NOT_BEGINS,
TCT_ENDS,
TCT_NOT_ENDS,
TCT_MATCHES,
TCT_NOT_MATCHES,
TCT_USER_ONLINE,
TCT_USER_OFFLINE,
TCT_USER_IDLE,
TCT_USER_IDLE_OFF,
TCT_USER_AWAY,
TCT_USER_AWAY_OFF
} GmTriggerConditionType;
typedef enum _GmTriggerActionType {
TAT_HIGHLIGHT_LINE = 0,
TAT_HIGHLIGHT_MATCH,
TAT_BEEP,
TAT_PLAY_SOUND,
TAT_NOTIFY,
TAT_RUN_SCRIPT,
TAT_RUN
} GmTriggerActionType;
typedef struct _GmTriggerData {
gint type;
gchar *data;
regex_t expr;
} GmTriggerData;
typedef struct _GmTrigger {
gchar *name;
GmTriggerType event;
GList *conditions;
GList *actions;
} GmTrigger;
GType gm_triggers_get_type(void) G_GNUC_CONST;
GmTriggers *gm_triggers_new(void);
GmTriggers *gm_triggers_new_from_file(gchar *filename);
void gm_triggers_save(GmTriggers *triggers);
void gm_triggers_save_as(GmTriggers *trg, const gchar *path);
void gm_triggers_set_path(GmTriggers *trg, gchar *path);
GmTriggers *gm_triggers_dup(GmTriggers *source);
const GList *gm_triggers_list(GmTriggers *trg);
void gm_triggers_add(GmTriggers *trg, GmTrigger *t);
void gm_triggers_clear(GmTriggers *trg);
GmTrigger *gm_trigger_new();
void gm_trigger_free(GmTrigger *trigger);
GmTrigger *gm_trigger_dup(GmTrigger *source);
void gm_trigger_set_name(GmTrigger *trigger, const gchar *name);
void gm_trigger_free_list(GList *list);
void gm_trigger_add_condition(GmTrigger *trigger, GmTriggerData *condition);
void gm_trigger_add_action(GmTrigger *trigger, GmTriggerData *action);
void gm_trigger_set_conditions(GmTrigger *trigger, GList *conditions);
void gm_trigger_set_actions(GmTrigger *trigger, GList *actions);
GmTriggerData *gm_trigger_data_new(gint type, gchar *data);
void gm_trigger_data_free(GmTriggerData *tdata);
G_END_DECLS
#endif /* __GM_TRIGGERS_H__ */

77
gnoemoe/gm-ui.h Normal file
View File

@ -0,0 +1,77 @@
#ifndef __GM_UI_H__
#define __GM_UI_H__
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#include <gtk/gtk.h>
#include "widgets/gm-app-view.h"
#include "gm-support.h"
G_BEGIN_DECLS
static const GtkActionEntry gm_sensitive_menu_entries[] =
{
/* Toplevel */
{"World", NULL, N_("_World")},
{"Edit", NULL, N_("_Edit")},
{"View", NULL, N_("_View")},
{"Help", NULL, N_("_Help")},
/* World menu */
{"WorldNew", GTK_STOCK_NEW, N_("New World..."), "<control>N",
N_("Create a new world"), G_CALLBACK(on_gm_app_view_world_new)},
{"WorldQuit", GTK_STOCK_QUIT, NULL, NULL,
N_("Quit the program"), G_CALLBACK(on_gm_app_view_world_quit)},
/* Edit menu */
{"EditWorlds", NULL, N_("Worlds..."), "<control>L",
N_("Edit worlds"), G_CALLBACK(on_gm_app_view_edit_worlds)},
{"EditPreferences", GTK_STOCK_PREFERENCES, NULL, NULL,
N_("Configure the application"),
G_CALLBACK(on_gm_app_view_edit_preferences)},
/* View menu */
{"ViewMcp", NULL, N_("MCP"), NULL,
N_("View MCP console"), G_CALLBACK(on_gm_app_view_view_mcp)},
{"ViewScripts", NULL, N_("Scripts"), NULL,
N_("View scripts"), G_CALLBACK(on_gm_app_view_view_scripts)},
/* Help menu */
{"HelpAbout", GTK_STOCK_ABOUT, NULL, NULL,
N_("About this application"), G_CALLBACK(on_gm_app_view_help_about)}
};
static const GtkActionEntry gm_menu_entries[] =
{
/* File menu */
{"WorldConnect", GTK_STOCK_NETWORK, N_("Connect"), "<control><shift>C",
N_("Connect or disconnect the current world"),
G_CALLBACK(on_gm_app_view_world_connect)},
{"WorldClose", GTK_STOCK_CLOSE, NULL, NULL,
N_("Close current world"), G_CALLBACK(on_gm_app_view_world_close)},
{"WorldLogs", GTK_STOCK_FILE, N_("Logs"), NULL,
N_("View current world logs"), G_CALLBACK(on_gm_app_view_world_logs)},
{"WorldInfo", GTK_STOCK_ABOUT, N_("In_fo"), NULL,
N_("View current world info"), G_CALLBACK(on_gm_app_view_world_info)},
/* Edit menu */
{"EditCut", GTK_STOCK_CUT, NULL, "<control>X",
N_("Cut the selection"), G_CALLBACK(on_gm_app_view_edit_cut)},
{"EditCopy", GTK_STOCK_COPY, NULL, "<control>C",
N_("Copy the selection"), G_CALLBACK(on_gm_app_view_edit_copy)},
{"EditPaste", GTK_STOCK_PASTE, NULL, "<control>V",
N_("Paste the clipboard"), G_CALLBACK(on_gm_app_view_edit_paste)},
{"EditWorld", NULL, N_("Current world..."), "<control>E",
N_("Edit the current world"), G_CALLBACK(on_gm_app_view_edit_world)},
{"EditFind", GTK_STOCK_FIND, NULL, "<control>F",
N_("Find text"), G_CALLBACK(on_gm_app_view_edit_find)},
{"EditFindNext", GTK_STOCK_FIND, N_("Find next"), "<control>G",
N_("Find next occurence"),
G_CALLBACK(on_gm_app_view_edit_find_next)},
};
G_END_DECLS
#endif /* __GEDIT_UI_H__ */

900
gnoemoe/gm-world.c Normal file
View File

@ -0,0 +1,900 @@
#include <string.h>
#include <errno.h>
#include <time.h>
#include "gm-world.h"
#include "gm-app.h"
#include "mcp/gm-mcp-session.h"
#include "gm-triggers.h"
#include "gm-marshal.h"
#include "gm-net.h"
#include "gm-bogus.h"
#include "gm-support.h"
#include "gm-debug.h"
#define GM_WORLD_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), \
GM_TYPE_WORLD, GmWorldPrivate))
void gm_world_save_input_history(GmWorld *world);
void gm_world_load_input_history(GmWorld *world);
void gm_world_load_triggers(GmWorld *world);
void on_gm_world_net_state_changing(GmNet *net, GmNetState state,
GmWorld *world);
void on_gm_world_net_net_error(GmNet *net, gchar *error, gint code,
GmWorld *world);
void on_gm_world_net_bytes_recv(GmNet *net, gchar *text, gint len,
GmWorld *world);
void on_gm_world_options_option_changed(GmOptions *options, gchar *key,
GmWorld *world);
struct _GmWorldPrivate {
gchar *path;
gboolean loaded;
gboolean active;
guint activity;
gchar *buffer;
gchar *status;
GmOptions *options;
GmTriggers *triggers;
GmNet *net;
GmMcpSession *mcp;
GList *history;
GList *editors;
GmWorldInfo info;
GmEditingInfo editing_info;
};
/* Signals */
enum {
ACTIVATE_REQUEST,
LOAD,
UNLOAD,
STATE_CHANGING,
WORLD_ERROR,
TEXT_RECEIVED,
EDITOR_ADDED,
EDITOR_REMOVED,
NAME_CHANGED,
ACTIVE_CHANGED,
ACTIVITY_CHANGED,
STATUS_CHANGED,
NUM_SIGNALS
};
static guint world_signals[NUM_SIGNALS] = {0};
G_DEFINE_TYPE(GmWorld, gm_world, G_TYPE_OBJECT)
static void
gm_world_finalize(GObject *object) {
GmWorld *world = GM_WORLD(object);
gchar *tmp_dir;
if (world->priv->path) {
gm_options_save(world->priv->options);
gm_world_save_input_history(world);
gm_triggers_save(world->priv->triggers);
// Removing all tmp files
tmp_dir = g_strconcat(world->priv->path, "/tmp", NULL);
gm_directory_remove_all(tmp_dir, FALSE);
g_free(tmp_dir);
}
g_list_free_simple(world->priv->history);
g_free(world->priv->path);
g_free(world->priv->buffer);
g_free(world->priv->status);
g_free(world->priv->editing_info.name);
g_free(world->priv->editing_info.upload);
g_list_free(world->priv->editing_info.lines);
g_object_unref(world->priv->triggers);
g_object_unref(world->priv->options);
g_object_unref(world->priv->net);
g_object_unref(world->priv->mcp);
G_OBJECT_CLASS(gm_world_parent_class)->finalize(object);
}
static void
gm_world_class_init(GmWorldClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = gm_world_finalize;
world_signals[ACTIVATE_REQUEST] =
g_signal_new("activate_request",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmWorldClass, activate_request),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
world_signals[LOAD] =
g_signal_new("load",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmWorldClass, load),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
world_signals[UNLOAD] =
g_signal_new("unload",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmWorldClass, unload),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
world_signals[STATE_CHANGING] =
g_signal_new("state_changing",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmWorldClass, state_changing),
NULL, NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE,
1,
G_TYPE_UINT);
world_signals[WORLD_ERROR] =
g_signal_new("world_error",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmWorldClass, world_error),
NULL, NULL,
gm_marshal_VOID__STRING_INT,
G_TYPE_NONE,
2,
G_TYPE_STRING,
G_TYPE_INT);
world_signals[TEXT_RECEIVED] =
g_signal_new("text_received",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmWorldClass, text_received),
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE,
1,
G_TYPE_STRING);
world_signals[EDITOR_ADDED] =
g_signal_new("editor_added",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmWorldClass, editor_added),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE,
1,
G_TYPE_OBJECT);
world_signals[EDITOR_REMOVED] =
g_signal_new("editor_removed",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmWorldClass, editor_removed),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE,
1,
G_TYPE_OBJECT);
world_signals[NAME_CHANGED] =
g_signal_new("name_changed",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmWorldClass, name_changed),
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE,
1,
G_TYPE_STRING);
world_signals[ACTIVE_CHANGED] =
g_signal_new("active_changed",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmWorldClass, active_changed),
NULL, NULL,
g_cclosure_marshal_VOID__BOOLEAN,
G_TYPE_NONE,
1,
G_TYPE_BOOLEAN);
world_signals[ACTIVITY_CHANGED] =
g_signal_new("activity_changed",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmWorldClass, activity_changed),
NULL, NULL,
g_cclosure_marshal_VOID__INT,
G_TYPE_NONE,
1,
G_TYPE_INT);
world_signals[STATUS_CHANGED] =
g_signal_new("status_changed",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmWorldClass, status_changed),
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE,
1,
G_TYPE_STRING);
g_type_class_add_private(object_class, sizeof(GmWorldPrivate));
}
void
gm_world_create_default_settings(GmWorld *world) {
const gchar *loc = gm_default_charset();
world->priv->options = gm_options_new();
gm_options_set(world->priv->options, "name", "");
gm_options_set(world->priv->options, "autoload", "0");
gm_options_set(world->priv->options, "host", "");
gm_options_set(world->priv->options, "port", "1111");
gm_options_set(world->priv->options, "player_name", "");
gm_options_set(world->priv->options, "reconnect", "0");
gm_options_set(world->priv->options, "password", "");
gm_options_set(world->priv->options, "charset", (gchar *)loc);
gm_options_set(world->priv->options, "history_length", "500");
// Non configurable options
gm_options_set(world->priv->options, "pane_position", "150");
}
static void
gm_world_init(GmWorld *world) {
world->priv = GM_WORLD_GET_PRIVATE(world);
gm_world_create_default_settings(world);
world->priv->path = NULL;
world->priv->loaded = FALSE;
world->priv->history = NULL;
world->priv->activity = 0;
world->priv->triggers = gm_triggers_new();
world->priv->net = gm_net_new();
world->priv->mcp = gm_mcp_session_new(G_OBJECT(world));
world->priv->buffer = NULL;
world->priv->editing_info.is_editing = FALSE;
world->priv->status = NULL;
g_signal_connect(world->priv->net, "state_changing",
G_CALLBACK(on_gm_world_net_state_changing), world);
g_signal_connect(world->priv->net, "net_error",
G_CALLBACK(on_gm_world_net_net_error), world);
g_signal_connect(world->priv->net, "bytes_recv",
G_CALLBACK(on_gm_world_net_bytes_recv), world);
g_signal_connect(world->priv->options, "option_changed",
G_CALLBACK(on_gm_world_options_option_changed), world);
}
void
gm_world_load_input_history(GmWorld *world) {
FILE *f;
gchar line[1024], *filename;
GString *str;
filename = g_strconcat(world->priv->path, G_DIR_SEPARATOR_S, "history",
NULL);
gm_debug_msg(DEBUG_DEFAULT, "GmWorld.LoadInputHistory: loading history (%s)!", filename);
if ((f = fopen(filename, "r")) != NULL) {
str = g_string_new("");
while (fgets(line, 1024 - 1, f) != NULL) {
g_string_append(str, line);
if (line[strlen(line) - 1] == '\n') {
if (line[1] != '\0') { // Empty lines, we don't need to process those
world->priv->history = g_list_append(world->priv->history,
g_strndup(str->str, strlen(str->str) - 1));
}
g_string_erase(str, 0, -1);
}
}
g_string_free(str, TRUE);
fclose(f);
} else {
gm_debug_msg(DEBUG_DEFAULT, "GmWorld.LoadInputHistory: could not retrieve contents of "
"file %s (%s)", filename, strerror(errno));
}
g_free(filename);
}
void
gm_world_save_input_history(GmWorld *world) {
FILE *f;
gchar *filename;
GList *elem;
if (world->priv->path == NULL) {
return;
}
filename = g_strconcat(world->priv->path, G_DIR_SEPARATOR_S, "history", NULL);
f = fopen(filename, "w");
if (f) {
gm_debug_msg(DEBUG_DEFAULT, "GmWorld.SaveInputHistory: saving input history to %s",
filename);
for (elem = world->priv->history; elem; elem = elem->next) {
fprintf(f, "%s\n", (gchar *) (elem->data));
}
fclose(f);
chmod(filename, 0660);
} else {
gm_debug_msg(DEBUG_DEFAULT, "GmWorld.SaveInputHistory: couldn't open history file (%s)"
" for saving: %s", filename, strerror(errno));
}
g_free(filename);
}
void
gm_world_load_triggers(GmWorld *world) {
gchar *path;
if (world->priv->triggers) {
g_object_unref(world->priv->triggers);
}
path = g_strconcat(world->priv->path, G_DIR_SEPARATOR_S, "triggers", NULL);
world->priv->triggers = gm_triggers_new_from_file(path);
g_free(path);
}
/* Public */
GmWorld *
gm_world_new(gchar *path) {
GmWorld *world = GM_WORLD(g_object_new(GM_TYPE_WORLD, NULL));
gchar *options_path;
if (path != NULL) {
options_path = g_strconcat(path, "/settings", NULL);
gm_debug_msg(DEBUG_DEFAULT, "GmWorld.new: creating new world for %s", path);
world->priv->path = g_strdup(path);
gm_debug_msg(DEBUG_DEFAULT, "GmWorld.new: creating default world settings for %s", path);
gm_options_load(world->priv->options, options_path);
if (strlen(gm_options_get(world->priv->options, "charset")) == 0) {
gm_options_set(world->priv->options, "charset",
gm_default_charset());
}
g_free(options_path);
gm_world_load_input_history(world);
gm_world_load_triggers(world);
}
/* CHECK: all done? */
return world;
}
GmWorld *
gm_world_dup(GmWorld *source) {
GmWorld *copy = gm_world_new(NULL);
g_object_unref(copy->priv->options);
copy->priv->options = gm_options_dup(source->priv->options);
copy->priv->triggers = gm_triggers_dup(source->priv->triggers);
return copy;
}
void
gm_world_load(GmWorld *world) {
if (world->priv->loaded) {
g_signal_emit(world, world_signals[ACTIVATE_REQUEST], 0);
return;
}
world->priv->loaded = TRUE;
g_signal_emit(world, world_signals[LOAD], 0);
g_signal_emit(world, world_signals[ACTIVATE_REQUEST], 0);
gm_world_connect(world);
}
void
gm_world_unload(GmWorld *world) {
//GList *w;
if (world->priv->loaded) {
world->priv->loaded = FALSE;
gm_world_disconnect(world);
//TODO: Destroy editors
//editors_close(wld);
g_signal_emit(world, world_signals[UNLOAD], 0);
}
}
GmTriggers *
gm_world_triggers(GmWorld *world) {
return world->priv->triggers;
}
GmOptions *
gm_world_options(GmWorld *world) {
return world->priv->options;
}
const gchar *
gm_world_name(GmWorld *world) {
return gm_options_get(world->priv->options, "name");
}
const gchar *
gm_world_path(GmWorld *world) {
return world->priv->path;
}
GList **
gm_world_history(GmWorld *world) {
return &(world->priv->history);
}
GmWorldInfo
gm_world_info(GmWorld *world) {
return world->priv->info;
}
gboolean
gm_world_loaded(GmWorld *world) {
return world->priv->loaded;
}
gboolean
gm_world_connected(GmWorld *world) {
return gm_net_state(world->priv->net) == GM_NET_STATE_CONNECTED;
}
gboolean
gm_world_disconnected(GmWorld *world) {
return gm_net_state(world->priv->net) == GM_NET_STATE_DISCONNECTED;
}
GmNetState
gm_world_state(GmWorld *world) {
return gm_net_state(world->priv->net);
}
const gchar *
gm_world_current_host(GmWorld *world) {
return gm_net_current_host(world->priv->net);
}
const gchar *
gm_world_current_port(GmWorld *world) {
return gm_net_current_port(world->priv->net);
}
void
gm_world_connect(GmWorld *world) {
/*if (strlen(gm_options_get(world->priv->options, "host")) == 0) {
g_signal_emit(world, world_signals[WORLD_ERROR], 0,
_("World has no host, please fill in a host first"));
} else if (strlen(gm_options_get(world->priv->options, "port")) == 0) {
g_signal_emit(world, world_signals[WORLD_ERROR], 0,
_("World has no port, please fill in a port first"));
} else {*/
gm_net_connect(world->priv->net,
gm_options_get(world->priv->options, "host"),
gm_options_get(world->priv->options, "port"));
//}
}
void
gm_world_connect_to(GmWorld *world, gchar *host, gchar *port) {
gm_net_connect(world->priv->net, host, port);
}
void
gm_world_disconnect(GmWorld *world) {
gm_net_disconnect(world->priv->net);
}
void
gm_world_log(GmWorld *world, GmLogType type, gchar *text) {
FILE *f;
GString *s;
gchar *start, *log, *no_ansi;
struct tm *timet;
time_t timer;
timer = time(0);
timet = localtime(&timer);
log = g_strdup_printf("%s/logs/%04d-%02d-%02d.log", world->priv->path,
timet->tm_year + 1900, timet->tm_mon + 1, timet->tm_mday);
f = fopen(log, "a");
g_free(log);
if (!f) {
return;
}
start = g_strdup_printf("[%02d:%02d:%02d] ", timet->tm_hour, timet->tm_min,
timet->tm_sec);
s = g_string_new(start);
g_free(start);
switch (type) {
case LOG_IN:
s = g_string_append_c(s, '<');
break;
case LOG_OUT:
s = g_string_append_c(s, '>');
break;
case LOG_MCP_IN:
s = g_string_append(s, "[MCP] <");
break;
case LOG_MCP_OUT:
s = g_string_append(s, "[MCP] >");
break;
case LOG_STATUS:
s = g_string_append_c(s, '#');
break;
}
s = g_string_append(s, " ");
s = g_string_append(s, text);
//no_ansi = gm_ansi_strip(g_strdup(s->str));
no_ansi = g_strdup(s->str);
if (no_ansi[strlen(no_ansi) - 1] != '\n') {
fputs(no_ansi, f);
fputc('\n', f);
} else {
fputs(no_ansi, f);
}
fclose(f);
g_free(no_ansi);
g_string_free(s, TRUE);
}
void
gm_world_parse_legacy_editing_start(GmWorld *world, gchar *line) {
gchar *name_start, *upload_start;
GmEditingInfo *einfo = &(world->priv->editing_info);
name_start = strstr(line, "name: ");
if (!name_start) {
return;
}
upload_start = strstr(line, " upload: ");
if (!upload_start) {
return;
}
einfo->is_editing = TRUE;
einfo->name = g_strndup(name_start + 6, (upload_start - name_start) - 6);
einfo->upload = g_strndup(upload_start + 9, (upload_start - line) + 9);
einfo->lines = NULL;
}
void
gm_world_process_line(GmWorld *world, gchar *line) {
gchar *non_text_start = NULL;
GmEditingInfo *einfo = &(world->priv->editing_info);
if (strncmp(line, "\x1B[0m", 4) == 0) {
non_text_start = g_strdup(line + 4);
} else {
non_text_start = g_strdup(line);
}
if (einfo->is_editing) {
if (strcmp(non_text_start, ".") == 0) {
//TODO: implementation, create new editor object, invoke signal
gm_world_add_editor(world, gm_editor_new(einfo->name, einfo->upload,
einfo->lines));
einfo->is_editing = FALSE;
g_free(einfo->name);
einfo->name = NULL;
g_free(einfo->upload);
einfo->upload = NULL;
g_list_free(einfo->lines);
einfo->lines = NULL;
} else {
einfo->lines = g_list_append(einfo->lines, g_strdup(non_text_start));
}
} else if (strncmp(non_text_start, "#$#", 3) == 0) {
if (strncasecmp(non_text_start + 3, " edit ", 6) == 0) {
gm_world_parse_legacy_editing_start(world, non_text_start + 9);
} else {
gm_mcp_session_handle_oob(world->priv->mcp, non_text_start + 3);
//gm_world_log(world, LOG_MCP_IN, non_text_start);
}
} else {
if (!gm_world_active(world)) {
gm_world_set_activity(world, world->priv->activity + 1);
}
if (strncmp(non_text_start, "#$\"", 3) == 0) {
if (strlen(non_text_start) != strlen(line)) {
g_signal_emit(world, world_signals[TEXT_RECEIVED], 0, "\x1B[0m");
}
gm_world_log(world, LOG_IN, non_text_start + 3);
g_signal_emit(world, world_signals[TEXT_RECEIVED], 0, non_text_start + 3);
g_signal_emit(world, world_signals[TEXT_RECEIVED], 0, "\n");
} else {
g_signal_emit(world, world_signals[TEXT_RECEIVED], 0, line);
g_signal_emit(world, world_signals[TEXT_RECEIVED], 0, "\n");
gm_world_log(world, LOG_IN, line);
}
}
g_free(non_text_start);
}
void
gm_world_process_input(GmWorld *world, gchar *text) {
#ifdef HAVE_RUBY
gchar *space, *script, *argstr;
gboolean script_ran = FALSE;
if (g_utf8_strlen(text, -1) > 1) {
if (text[0] == '/' && text[1] != '/') {
space = strstr(text, " ");
if (space == NULL) {
script = g_strdup(text + 1);
argstr = g_strdup("");
} else {
script = g_strndup(text + 1, (space - text) - 1);
argstr = g_strdup(space + 1);
}
gm_debug_msg(DEBUG_DEFAULT, "GmWorld.ProcessInput: Trying script %s (%s)", script,
argstr);
script_ran = gm_scripts_run(gm_app_scripts(gm_app_instance()),
world, script, argstr);
g_free(script);
g_free(argstr);
if (script_ran) {
return;
}
} else if (text[0] == '/' && text[1] == '/') {
text = text + 1;
}
}
#endif
gm_world_sendln(world, text);
}
void
gm_world_writeln(GmWorld *world, gchar *text) {
gchar *newline = g_strconcat(text, "\n", NULL);
g_signal_emit(world, world_signals[TEXT_RECEIVED], 0, newline);
g_free(newline);
}
void
gm_world_remove_editor(GmWorld *world, GmEditor *editor) {
g_return_if_fail(g_list_find(world->priv->editors, editor) != NULL);
world->priv->editors = g_list_remove(world->priv->editors, editor);
g_signal_emit(world, world_signals[EDITOR_REMOVED], 0, G_OBJECT(editor));
g_object_unref(editor);
}
void
gm_world_add_editor(GmWorld *world, GmEditor *editor) {
world->priv->editors = g_list_append(world->priv->editors, editor);
g_signal_emit(world, world_signals[EDITOR_ADDED], 0, G_OBJECT(editor));
}
void
gm_world_sendln(GmWorld *world, gchar *text) {
gchar *normal;
// Convert text from utf-8 to the correct locale
normal = gm_to_utf8_with_fallback(text, -1,
gm_options_get(world->priv->options, "charset"), "?");
if (!normal) {
gm_debug_msg(DEBUG_DEFAULT, "GmWorld.Send: conversion failed!");
normal = g_strdup(text);
}
gm_world_log(world, LOG_OUT, text);
gm_net_send_line(world->priv->net, normal);
g_free(normal);
}
void
gm_world_set_active(GmWorld *world, gboolean active) {
world->priv->active = active;
g_signal_emit(world, world_signals[ACTIVE_CHANGED], 0, active);
if (active) {
gm_world_set_activity(world, 0);
}
}
gboolean
gm_world_active(GmWorld *world) {
return world->priv->active;
}
void
gm_world_set_activity(GmWorld *world, gint activity) {
world->priv->activity = activity;
g_signal_emit(world, world_signals[ACTIVITY_CHANGED], 0, activity);
}
gint
gm_world_activity(GmWorld *world) {
return world->priv->activity;
}
void
gm_world_name_changed(GmWorld *world) {
gchar *tmp_path = world->priv->path ? g_strdup(world->priv->path) : NULL;
gchar *new_path = g_strconcat(gm_app_worlds_path(gm_app_instance()), "/",
gm_options_get(world->priv->options, "name"), NULL);
if (tmp_path && strcmp(tmp_path, new_path) == 0) {
g_free(new_path);
g_free(tmp_path);
return;
}
g_free(world->priv->path);
world->priv->path = new_path;
if (tmp_path && g_file_test(tmp_path, G_FILE_TEST_EXISTS)) {
// Then! rename the old_path to the new_path
rename(tmp_path, world->priv->path);
} else if (!g_file_test(world->priv->path, G_FILE_TEST_EXISTS)) {
// There was no old path, so a new dir should be made
mkdir(world->priv->path, 0755);
}
g_free(tmp_path);
tmp_path = g_strconcat(world->priv->path, G_DIR_SEPARATOR_S, "settings",
NULL);
gm_options_save_as(world->priv->options, tmp_path);
g_free(tmp_path);
tmp_path = g_strconcat(world->priv->path, G_DIR_SEPARATOR_S, "triggers",
NULL);
gm_triggers_save_as(world->priv->triggers, tmp_path);
g_free(tmp_path);
g_signal_emit(world, world_signals[NAME_CHANGED], 0,
gm_options_get(world->priv->options, "name"));
}
void
gm_world_set_status(GmWorld *world, gchar const *status) {
g_free(world->priv->status);
world->priv->status = g_strdup(status);
g_signal_emit(world, world_signals[STATUS_CHANGED], 0,
world->priv->status);
}
gchar const *
gm_world_get_status(GmWorld *world) {
return world->priv->status;
}
/* Callbacks */
void
on_gm_world_net_state_changing(GmNet *net, GmNetState state, GmWorld *world) {
g_signal_emit(world, world_signals[STATE_CHANGING], 0, state);
}
void
on_gm_world_net_net_error(GmNet *net, gchar *error, gint code,
GmWorld *world) {
g_signal_emit(world, world_signals[WORLD_ERROR], 0, error, code);
}
void
on_gm_world_net_bytes_recv(GmNet *net, gchar *text, gint len,
GmWorld *world) {
gchar *all, *utext, *ptr, *line, *p;
gint i;
utext = gm_to_utf8_with_fallback(text, len,
gm_options_get(world->priv->options, "charset"), "?");
if (!utext) {
gm_debug_msg(DEBUG_DEFAULT, "GmWorld.NetBytesRecv: conversion failed!");
utext = g_strndup(text, len);
}
if (world->priv->buffer != NULL) {
all = g_strconcat(world->priv->buffer, utext, NULL);
g_free(utext);
g_free(world->priv->buffer);
world->priv->buffer = NULL;
} else {
all = utext;
}
// TODO: UTF-8 compliant
line = (gchar *)(malloc((strlen(all) * sizeof(gchar)) + 1));
i = 0;
p = all;
/* Find lines in `all' and process them */
for (ptr = all; *ptr != '\0'; ptr++) {
if (*ptr == '\n') {
line[i] = '\0';
line[i + 1] = '\0';
gm_world_process_line(world, line);
p = ptr + 1;
i = 0;
} else if (*ptr != '\r') {
line[i] = *ptr;
i++;
}
}
if (i > 0) {
world->priv->buffer = g_strdup(p);
}
g_free(all);
}
void
on_gm_world_options_option_changed(GmOptions *options, gchar *key,
GmWorld *world) {
if (strcmp(key, "name") == 0) {
gm_world_name_changed(world);
}
}

140
gnoemoe/gm-world.h Normal file
View File

@ -0,0 +1,140 @@
#ifndef __GM_WORLD_H__
#define __GM_WORLD_H__
#include <gtk/gtk.h>
#include "gm-options.h"
#include "gm-net.h"
#include "gm-editor.h"
#include "gm-triggers.h"
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_WORLD (gm_world_get_type())
#define GM_WORLD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD, GmWorld))
#define GM_WORLD_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD, GmWorld const))
#define GM_WORLD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_WORLD, GmWorldClass))
#define GM_IS_WORLD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_WORLD))
#define GM_IS_WORLD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_WORLD))
#define GM_WORLD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_WORLD, GmWorldClass))
typedef struct _GmWorldInfo {
gchar *homepage;
gchar *location;
gchar *admin;
gchar *contact;
gchar *charset;
gchar *language;
gchar *system;
gchar *logo;
} GmWorldInfo;
/** \ingroup world
* \brief struct for world editing information
*
* Struct which contains various fields for storing editing information. This
* is only used if editing via mcp_simpleedit can't be used.
*/
typedef struct _GmEditingInfo {
gboolean is_editing; /**< are we editing at the moment? */
GList *lines; /**< the lines to be edited */
gchar *name; /**< the name of the editor */
gchar *upload; /**< the command to send when sending the editor contents */
} GmEditingInfo;
/** \ingroup world
* \brief enum indicating log type
*
* Enumeration which indicates the log type
*/
typedef enum _GmLogType GmLogType;
enum _GmLogType {
LOG_IN, /**< incoming lines */
LOG_OUT, /**< outgoing lines */
LOG_MCP_IN, /**< mcp incoming lines */
LOG_MCP_OUT, /**< mcp outgoing lines */
LOG_STATUS /**< status lines (such as connecting information) */
};
/* Private structure type */
typedef struct _GmWorldPrivate GmWorldPrivate;
/*
* Main object structure
*/
typedef struct _GmWorld GmWorld;
struct _GmWorld {
GObject object;
/*< private > */
GmWorldPrivate *priv;
};
/*
* Class definition
*/
typedef struct _GmWorldClass GmWorldClass;
struct _GmWorldClass {
GObjectClass parent_class;
/* Signals */
void (* activate_request) (GmWorld *world);
void (* load) (GmWorld *world);
void (* unload) (GmWorld *world);
void (* state_changing) (GmWorld *world, guint state);
void (* world_error) (GmWorld *world, gchar const *error, gint code);
void (* text_received) (GmWorld *world, gchar const *text);
void (* editor_added) (GmWorld *world, GObject *editor);
void (* editor_removed) (GmWorld *world, GObject *editor);
void (* name_changed) (GmWorld *world, gchar const *name);
void (* active_changed) (GmWorld *world, gboolean active);
void (* activity_changed) (GmWorld *world, gint activity);
void (* status_changed) (GmWorld *world, gchar const *status);
};
GType gm_world_get_type(void) G_GNUC_CONST;
GmWorld *gm_world_new(gchar *path);
GmWorld *gm_world_dup(GmWorld *source);
void gm_world_load(GmWorld *world);
void gm_world_unload(GmWorld *world);
const gchar *gm_world_name(GmWorld *world);
GmOptions *gm_world_options(GmWorld *world);
GmWorldInfo gm_world_info(GmWorld *world);
const gchar *gm_world_path(GmWorld *world);
GList **gm_world_history(GmWorld *world);
gint gm_world_activity(GmWorld *world);
gboolean gm_world_active(GmWorld *world);
GmTriggers *gm_world_triggers(GmWorld *world);
const gchar *gm_world_current_host(GmWorld *world);
const gchar *gm_world_current_port(GmWorld *world);
void gm_world_set_name(GmWorld *world, const gchar *name);
void gm_world_set_activity(GmWorld *world, gint activity);
void gm_world_set_active(GmWorld *world, gboolean active);
void gm_world_set_status(GmWorld *world, gchar const *status);
gchar const *gm_world_get_status(GmWorld *world);
gboolean gm_world_loaded(GmWorld *world);
GmNetState gm_world_state(GmWorld *world);
gboolean gm_world_connected(GmWorld *world);
gboolean gm_world_disconnected(GmWorld *world);
void gm_world_connect(GmWorld *world);
void gm_world_connect_to(GmWorld *world, gchar *host, gchar *port);
void gm_world_disconnect(GmWorld *world);
void gm_world_add_editor(GmWorld *world, GmEditor *editor);
void gm_world_remove_editor(GmWorld *world, GmEditor *editor);
void gm_world_sendln(GmWorld *world, gchar *text);
void gm_world_writeln(GmWorld *world, gchar *text);
void gm_world_process_input(GmWorld *world, gchar *text);
void gm_world_log(GmWorld *world, GmLogType type, gchar *text);
G_END_DECLS
#endif /* __GM_WORLD_H__ */

59
gnoemoe/gtktemplate.c Normal file
View File

@ -0,0 +1,59 @@
#include <gtk/gtk.h>
#include "gm-{template-}.h"
#define GM_{TEMPLATE}_GET_PRIVATE(object)( \
G_TYPE_INSTANCE_GET_PRIVATE((object), \
GM_TYPE_{TEMPLATE}, Gm{Template}Private))
struct _Gm{Template}Private {
};
/* Signals
enum {
PROTO
NUM_SIGNALS
};
static guint gm_{template_}_signals[NUM_SIGNALS] = {0};*/
G_DEFINE_TYPE(Gm{Template}, gm_{template_}, GTK_TYPE_{PARENT})
static void
gm_{template_}_finalize(GObject *object) {
//Gm{Template} *obj = GM_{TEMPLATE}(object);
G_OBJECT_CLASS(gm_{template_}_parent_class)->finalize(object);
}
static void
gm_{template_}_class_init(Gm{Template}Class *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = gm_{template_}_finalize;
/*gm_{template_}_signals[PROTO] =
g_signal_new("proto",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(Gm{Template}Class, proto),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);*/
g_type_class_add_private(object_class, sizeof(Gm{Template}Private));
}
static void
gm_{template_}_init(Gm{Template} *obj) {
obj->priv = GM_{TEMPLATE}_GET_PRIVATE(obj);
}
Gm{Template} *
gm_{template_}_new() {
Gm{Template} *obj = GM_{TEMPLATE}(g_object_new(GM_TYPE_{TEMPLATE}, NULL));
return obj;
}

56
gnoemoe/gtktemplate.h Normal file
View File

@ -0,0 +1,56 @@
#ifndef __GM_{TEMPLATE}_H__
#define __GM_{TEMPLATE}_H__
#include <gtk/gtk.h>
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_{TEMPLATE} (gm_{template_}_get_type())
#define GM_{TEMPLATE}(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
GM_TYPE_{TEMPLATE}, Gm{Template}))
#define GM_{TEMPLATE}_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
GM_TYPE_{TEMPLATE}, Gm{Template} const))
#define GM_{TEMPLATE}_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
GM_TYPE_{TEMPLATE}, Gm{Template}Class))
#define GM_IS_{TEMPLATE}(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
GM_TYPE_{TEMPLATE}))
#define GM_IS_{TEMPLATE}_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
GM_TYPE_{TEMPLATE}))
#define GM_{TEMPLATE}_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
GM_TYPE_{TEMPLATE}, Gm{Template}Class))
/* Private structure type */
typedef struct _Gm{Template}Private Gm{Template}Private;
/*
* Main object structure
*/
typedef struct _Gm{Template} Gm{Template};
struct _Gm{Template} {
Gtk{Parent} parent;
/*< private > */
Gm{Template}Private *priv;
};
/*
* Class definition
*/
typedef struct _Gm{Template}Class Gm{Template}Class;
struct _Gm{Template}Class {
Gtk{Parent}Class parent_class;
/* Signals
void (* proto) (Gm{Template} *obj); */
};
GType gm_{template_}_get_type(void) G_GNUC_CONST;
Gm{Template} *gm_{template_}_new(void);
G_END_DECLS
#endif /* __GM_{TEMPLATE}_H__ */

View File

@ -0,0 +1,13 @@
## Process this file with automake to produce Makefile.in
mcpdir = mcp
gnoemoe_SOURCES += $(mcpdir)/gm-mcp-classes.c \
$(mcpdir)/gm-mcp.c $(mcpdir)/gm-mcp.h \
$(mcpdir)/gm-mcp-session.c $(mcpdir)/gm-mcp-session.h \
$(mcpdir)/gm-mcp-package.c $(mcpdir)/gm-mcp-package.h \
$(mcpdir)/gm-mcp-negotiate.c $(mcpdir)/gm-mcp-negotiate.h \
$(mcpdir)/gm-mcp-awns-status.c $(mcpdir)/gm-mcp-awns-status.h
$(mcpdir)/gm-mcp-classes.c: $(mcpdir)/packages.defs
( cd $(srcdir) && ./$(mcpdir)/mcpinit.rb \
$(mcpdir)/packages.defs $(mcpdir)/gm-mcp-classes ) > $@

View File

@ -0,0 +1,82 @@
#include <glib-object.h>
#include "gm-mcp-awns-status.h"
#include "gm-mcp-session.h"
#include "gm-mcp.h"
#include "../gm-world.h"
#include "../gm-debug.h"
#define GM_MCP_AWNS_STATUS_GET_PRIVATE(object)( \
G_TYPE_INSTANCE_GET_PRIVATE((object), \
GM_TYPE_MCP_AWNS_STATUS, GmMcpAwnsStatusPrivate))
struct _GmMcpAwnsStatusPrivate {
};
/* Signals
enum {
PROTO
NUM_SIGNALS
};
static guint gm_mcp_awns_status_signals[NUM_SIGNALS] = {0};*/
void gm_mcp_awns_status_handle_simple(GmMcpPackage *package, gchar *suffix,
GList *fields);
G_DEFINE_TYPE(GmMcpAwnsStatus, gm_mcp_awns_status, GM_TYPE_MCP_PACKAGE)
static void
gm_mcp_awns_status_finalize(GObject *object) {
//GmMcpAwnsStatus *obj = GM_MCP_AWNS_STATUS(object);
G_OBJECT_CLASS(gm_mcp_awns_status_parent_class)->finalize(object);
}
static void
gm_mcp_awns_status_class_init(GmMcpAwnsStatusClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
GmMcpPackageClass *pklass = GM_MCP_PACKAGE_CLASS(klass);
object_class->finalize = gm_mcp_awns_status_finalize;
/*gm_mcp_awns_status_signals[PROTO] =
g_signal_new("proto",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmMcpAwnsStatusClass, proto),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);*/
pklass->name = "dns-com-awns-status";
pklass->handle_simple = &gm_mcp_awns_status_handle_simple;
g_type_class_add_private(object_class, sizeof(GmMcpAwnsStatusPrivate));
}
static void
gm_mcp_awns_status_init(GmMcpAwnsStatus *obj) {
obj->priv = GM_MCP_AWNS_STATUS_GET_PRIVATE(obj);
}
/* Public */
GmMcpAwnsStatus *
gm_mcp_awns_status_new() {
GmMcpAwnsStatus *obj = GM_MCP_AWNS_STATUS(g_object_new(
GM_TYPE_MCP_AWNS_STATUS, NULL));
return obj;
}
/* Private */
void
gm_mcp_awns_status_handle_simple(GmMcpPackage *package, gchar *suffix,
GList *fields) {
gchar const *text = gm_mcp_find_value(fields, "text");
GmMcpSession *session = GM_MCP_PACKAGE_SESSION(package);
gm_debug_msg(DEBUG_MCP, "GmMcpAwnsStatus.HandleSimple: set status %s", text);
gm_world_set_status(GM_MCP_SESSION_WORLD(session), text);
}

View File

@ -0,0 +1,57 @@
#ifndef __GM_MCP_AWNS_STATUS_H__
#define __GM_MCP_AWNS_STATUS_H__
#include <glib-object.h>
#include "gm-mcp-package.h"
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_MCP_AWNS_STATUS (gm_mcp_awns_status_get_type())
#define GM_MCP_AWNS_STATUS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
GM_TYPE_MCP_AWNS_STATUS, GmMcpAwnsStatus))
#define GM_MCP_AWNS_STATUS_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
GM_TYPE_MCP_AWNS_STATUS, GmMcpAwnsStatus const))
#define GM_MCP_AWNS_STATUS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
GM_TYPE_MCP_AWNS_STATUS, GmMcpAwnsStatusClass))
#define GM_IS_MCP_AWNS_STATUS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
GM_TYPE_MCP_AWNS_STATUS))
#define GM_IS_MCP_AWNS_STATUS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
GM_TYPE_MCP_AWNS_STATUS))
#define GM_MCP_AWNS_STATUS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
GM_TYPE_MCP_AWNS_STATUS, GmMcpAwnsStatusClass))
/* Private structure type */
typedef struct _GmMcpAwnsStatusPrivate GmMcpAwnsStatusPrivate;
/*
* Main object structure
*/
typedef struct _GmMcpAwnsStatus GmMcpAwnsStatus;
struct _GmMcpAwnsStatus {
GmMcpPackage parent;
/*< private > */
GmMcpAwnsStatusPrivate *priv;
};
/*
* Class definition
*/
typedef struct _GmMcpAwnsStatusClass GmMcpAwnsStatusClass;
struct _GmMcpAwnsStatusClass {
GmMcpPackageClass parent_class;
/* Signals
void (* proto) (GmMcpAwnsStatus *obj); */
};
GType gm_mcp_awns_status_get_type(void) G_GNUC_CONST;
GmMcpAwnsStatus *gm_mcp_awns_status_new(void);
G_END_DECLS
#endif /* __GM_MCP_AWNS_STATUS_H__ */

View File

@ -0,0 +1,249 @@
#include <glib-object.h>
#include <string.h>
#include "gm-mcp-negotiate.h"
#include "gm-mcp.h"
#include "gm-mcp-session.h"
#include "gm-mcp-package.h"
#include "../gm-debug.h"
#include "../gm-support.h"
#define GM_MCP_NEGOTIATE_GET_PRIVATE(object)( \
G_TYPE_INSTANCE_GET_PRIVATE((object), \
GM_TYPE_MCP_NEGOTIATE, GmMcpNegotiatePrivate))
typedef struct _PackageInfo {
GmMcpPackageClass *klass;
gdouble version;
} PackageInfo;
struct _GmMcpNegotiatePrivate {
GList *packages;
};
/* Signals
enum {
PROTO
NUM_SIGNALS
};
static guint gm_mcp_negotiate_signals[NUM_SIGNALS] = {0};*/
void gm_mcp_negotiate_handle_simple(GmMcpPackage *package, gchar *suffix,
GList *fields);
G_DEFINE_TYPE(GmMcpNegotiate, gm_mcp_negotiate, GM_TYPE_MCP_PACKAGE);
static void
gm_mcp_negotiate_finalize(GObject *object) {
//GmMcpNegotiate *obj = GM_MCP_NEGOTIATE(object);
G_OBJECT_CLASS(gm_mcp_negotiate_parent_class)->finalize(object);
}
static void
gm_mcp_negotiate_class_init(GmMcpNegotiateClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
GmMcpPackageClass *pklass = GM_MCP_PACKAGE_CLASS(klass);
object_class->finalize = gm_mcp_negotiate_finalize;
/*gm_mcp_negotiate_signals[PROTO] =
g_signal_new("proto",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmMcpNegotiateClass, proto),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);*/
pklass->name = "mcp-negotiate";
pklass->handle_simple = &gm_mcp_negotiate_handle_simple;
g_type_class_add_private(object_class, sizeof(GmMcpNegotiatePrivate));
}
static void
gm_mcp_negotiate_init(GmMcpNegotiate *obj) {
obj->priv = GM_MCP_NEGOTIATE_GET_PRIVATE(obj);
obj->priv->packages = NULL;
}
PackageInfo *
gm_mcp_negotiate_find_package(GmMcpNegotiate *package, gchar *name) {
PackageInfo *pinfo;
GList *elem;
for (elem = package->priv->packages; elem; elem = elem->next) {
pinfo = (PackageInfo *)(elem->data);
if (strcasecmp(pinfo->klass->name, name) == 0) {
return pinfo;
}
}
return NULL;
}
void
gm_mcp_negotiate_fix_overrides(GmMcpNegotiate *package) {
GList *l, *item, *over;
PackageInfo *pinfo, *pover;
gchar *name;
l = g_list_copy(package->priv->packages);
for (item = l; item; item = item->next) {
pinfo = (PackageInfo *)(item->data);
for (over = pinfo->klass->overrides; over; over = over->next) {
name = (gchar *)(over->data);
if ((pover = gm_mcp_negotiate_find_package(package, name))) {
gm_debug_msg(DEBUG_MCP, "GmMcpNegotiate.FixOverrides: package %s "
"overrides %s", pinfo->klass->name, pover->klass->name);
package->priv->packages = g_list_remove(
package->priv->packages, pover);
}
}
}
g_list_free(l);
}
void
gm_mcp_negotiate_fix_depends(GmMcpNegotiate *package) {
PackageInfo *pinfo, *pdep;
GList *l, *item, *dep;
gchar *name;
l = g_list_copy(package->priv->packages);
for (item = l; item; item = item->next) {
pinfo = (PackageInfo *)(item->data);
for (dep = pinfo->klass->depends; dep; dep = dep->next) {
name = (gchar *)(dep->data);
if (!(pdep = gm_mcp_negotiate_find_package(package, name))) {
gm_debug_msg(DEBUG_MCP, "GmMcpNegotiate.FixDepends: package %s depends "
"on %s, but %s is not supported", pinfo->klass->name,
name, name);
// Remove package because depencendies are not met
package->priv->packages = g_list_remove(
package->priv->packages, pinfo);
break;
} else {
// Make sure this dependency is loaded before the package
package->priv->packages = g_list_remove(
package->priv->packages, pdep);
package->priv->packages = g_list_insert_before(
package->priv->packages,
g_list_find(package->priv->packages, pinfo), pdep);
}
}
}
g_list_free(l);
}
/* Public */
GmMcpNegotiate *
gm_mcp_negotiate_new() {
GmMcpNegotiate *obj = GM_MCP_NEGOTIATE(g_object_new(GM_TYPE_MCP_NEGOTIATE,
NULL));
return obj;
}
/* Private */
gboolean
gm_mcp_negotiate_send_can(GmMcpPackageClass *klass, gpointer user_data) {
GmMcpNegotiate *package = GM_MCP_NEGOTIATE(user_data);
gchar min_v[16], max_v[16];
g_ascii_formatd(min_v, 16, "%.1f", klass->min_version);
g_ascii_formatd(max_v, 16, "%.1f", klass->max_version);
gm_mcp_session_send_simple(GM_MCP_PACKAGE_SESSION(package),
"mcp-negotiate-can", "package", klass->name, "min-version",
min_v, "max-version", max_v, NULL);
return FALSE;
}
void
gm_mcp_negotiate_handle_simple(GmMcpPackage *package, gchar *suffix,
GList *fields) {
gchar const *pname;
GmMcpPackageClass *pklass;
GmMcpNegotiate *negotiate = GM_MCP_NEGOTIATE(package);
PackageInfo *pinfo;
double version;
gchar const *min_v, *max_v;
gdouble cmin = 0.0, cmax = 0.0;
GList *elem;
if (strcasecmp(suffix, "can") == 0) {
// Fields has package, min-version, max-version
pname = gm_mcp_find_value(fields, "package");
pklass = gm_mcp_session_find_package_class(pname);
if (pklass) {
min_v = gm_mcp_find_value(fields, "min-version");
if (min_v) {
cmin = g_ascii_strtod(min_v, NULL);
}
max_v = gm_mcp_find_value(fields, "max-version");
if (max_v) {
cmax = g_ascii_strtod(max_v, NULL);
}
version = gm_mcp_get_version(pklass->min_version,
pklass->max_version, cmin, cmax);
if (version > 0.0) {
gm_debug_msg(DEBUG_MCP, "GmMcpNegotiate.HandleSimple: %s, "
"package is supported", pname);
pinfo = g_new(PackageInfo, 1);
pinfo->klass = pklass;
pinfo->version = version;
negotiate->priv->packages = g_list_append(
negotiate->priv->packages, pinfo);
} else {
gm_debug_msg(DEBUG_MCP, "GmMcpNegotiate.HandleSimple: %s, package "
"supported but wrong version!", pname);
}
} else {
gm_debug_msg(DEBUG_MCP, "GmMcpNegotiate.HandleSimple: %s, package is not "
"supported!", pname);
}
} else if (strcasecmp(suffix, "end") == 0) {
gm_mcp_session_package_class_for_each(gm_mcp_negotiate_send_can,
(gpointer)(negotiate));
gm_mcp_session_send_simple(GM_MCP_PACKAGE_SESSION(negotiate),
"mcp-negotiate-end", NULL);
gm_mcp_negotiate_fix_overrides(negotiate);
gm_mcp_negotiate_fix_depends(negotiate);
for (elem = negotiate->priv->packages; elem; elem = elem->next) {
pinfo = (PackageInfo *)(elem->data);
gm_mcp_session_create_package(GM_MCP_PACKAGE_SESSION(negotiate),
pinfo->klass, pinfo->version);
g_free(pinfo);
}
g_list_free(negotiate->priv->packages);
negotiate->priv->packages = NULL;
}
}

View File

@ -0,0 +1,57 @@
#ifndef __GM_MCP_NEGOTIATE_H__
#define __GM_MCP_NEGOTIATE_H__
#include <glib-object.h>
#include "gm-mcp-package.h"
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_MCP_NEGOTIATE (gm_mcp_negotiate_get_type())
#define GM_MCP_NEGOTIATE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
GM_TYPE_MCP_NEGOTIATE, GmMcpNegotiate))
#define GM_MCP_NEGOTIATE_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
GM_TYPE_MCP_NEGOTIATE, GmMcpNegotiate const))
#define GM_MCP_NEGOTIATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
GM_TYPE_MCP_NEGOTIATE, GmMcpNegotiateClass))
#define GM_IS_MCP_NEGOTIATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
GM_TYPE_MCP_NEGOTIATE))
#define GM_IS_MCP_NEGOTIATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
GM_TYPE_MCP_NEGOTIATE))
#define GM_MCP_NEGOTIATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
GM_TYPE_MCP_NEGOTIATE, GmMcpNegotiateClass))
/* Private structure type */
typedef struct _GmMcpNegotiatePrivate GmMcpNegotiatePrivate;
/*
* Main object structure
*/
typedef struct _GmMcpNegotiate GmMcpNegotiate;
struct _GmMcpNegotiate {
GmMcpPackage parent;
/*< private > */
GmMcpNegotiatePrivate *priv;
};
/*
* Class definition
*/
typedef struct _GmMcpNegotiateClass GmMcpNegotiateClass;
struct _GmMcpNegotiateClass {
GmMcpPackageClass parent_class;
/* Signals
void (* proto) (GmMcpNegotiate *obj); */
};
GType gm_mcp_negotiate_get_type(void) G_GNUC_CONST;
GmMcpNegotiate *gm_mcp_negotiate_new(void);
G_END_DECLS
#endif /* __GM_MCP_NEGOTIATE_H__ */

View File

@ -0,0 +1,140 @@
#include <glib-object.h>
#include "gm-mcp-session.h"
#include "gm-mcp-package.h"
#include "../gm-support.h"
#define GM_MCP_PACKAGE_GET_PRIVATE(object)( \
G_TYPE_INSTANCE_GET_PRIVATE((object), \
GM_TYPE_MCP_PACKAGE, GmMcpPackagePrivate))
struct _GmMcpPackagePrivate {
GmMcpSession *session;
gdouble version;
};
/* Signals
enum {
PROTO
NUM_SIGNALS
};
static guint gm_mcp_package_signals[NUM_SIGNALS] = {0};*/
G_DEFINE_TYPE(GmMcpPackage, gm_mcp_package, G_TYPE_OBJECT)
static void
gm_mcp_package_finalize(GObject *object) {
//GmMcpPackage *package = GM_MCP_PACKAGE(object);
G_OBJECT_CLASS(gm_mcp_package_parent_class)->finalize(object);
}
static void
gm_mcp_package_class_init(GmMcpPackageClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = gm_mcp_package_finalize;
/*gm_mcp_package_signals[PROTO] =
g_signal_new("proto",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmMcpPackageClass, proto),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);*/
klass->handle_simple = NULL;
klass->handle_multi = NULL;
klass->depends = NULL;
klass->overrides = NULL;
klass->name = NULL;
klass->min_version = 1.0;
klass->max_version = 1.0;
g_type_class_add_private(object_class, sizeof(GmMcpPackagePrivate));
}
static void
gm_mcp_package_init(GmMcpPackage *obj) {
obj->priv = GM_MCP_PACKAGE_GET_PRIVATE(obj);
obj->priv->session = NULL;
obj->priv->version = 1.0;
}
/* Public */
GmMcpPackage *
gm_mcp_package_new() {
GmMcpPackage *obj = GM_MCP_PACKAGE(g_object_new(GM_TYPE_MCP_PACKAGE, NULL));
return obj;
}
void
gm_mcp_package_handle_simple(GmMcpPackage *package, gchar *suffix,
GList *fields) {
return GM_MCP_PACKAGE_GET_CLASS(package)->handle_simple(package,
suffix, fields);
}
gboolean
gm_mcp_package_can_handle_simple(GmMcpPackage *package) {
return (GM_MCP_PACKAGE_GET_CLASS(package)->handle_simple != NULL);
}
gboolean
gm_mcp_package_handle_multi(GmMcpPackage *package, gchar *data_tag,
gchar *key, gchar *value, GList *allValues) {
return GM_MCP_PACKAGE_GET_CLASS(package)->handle_multi(package,
data_tag, key, value, allValues);
}
gboolean
gm_mcp_package_can_handle_multi(GmMcpPackage *package) {
return (GM_MCP_PACKAGE_GET_CLASS(package)->handle_multi != NULL);
}
void
gm_mcp_package_set_session(GmMcpPackage *package, GObject *session) {
package->priv->session = GM_MCP_SESSION(session);
}
GObject *
gm_mcp_package_get_session(GmMcpPackage *package) {
return G_OBJECT(package->priv->session);
}
void
gm_mcp_package_set_version(GmMcpPackage *package, gdouble version) {
package->priv->version = version;
}
gdouble
gm_mcp_package_get_version(GmMcpPackage *package) {
return package->priv->version;
}
gchar const *
gm_mcp_package_get_name(GmMcpPackage *package) {
return GM_MCP_PACKAGE_GET_CLASS(package)->name;
}
GList const *
gm_mcp_package_get_depends(GmMcpPackage *package) {
return GM_MCP_PACKAGE_GET_CLASS(package)->depends;
}
GList const *
gm_mcp_package_get_overrides(GmMcpPackage *package) {
return GM_MCP_PACKAGE_GET_CLASS(package)->overrides;
}
gdouble
gm_mcp_package_get_min_version(GmMcpPackage *package) {
return GM_MCP_PACKAGE_GET_CLASS(package)->min_version;
}
gdouble
gm_mcp_package_get_max_version(GmMcpPackage *package) {
return GM_MCP_PACKAGE_GET_CLASS(package)->max_version;
}

View File

@ -0,0 +1,89 @@
#ifndef __GM_MCP_PACKAGE_H__
#define __GM_MCP_PACKAGE_H__
#include <glib-object.h>
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_MCP_PACKAGE (gm_mcp_package_get_type())
#define GM_MCP_PACKAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
GM_TYPE_MCP_PACKAGE, GmMcpPackage))
#define GM_MCP_PACKAGE_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
GM_TYPE_MCP_PACKAGE, GmMcpPackage const))
#define GM_MCP_PACKAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
GM_TYPE_MCP_PACKAGE, GmMcpPackageClass))
#define GM_IS_MCP_PACKAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
GM_TYPE_MCP_PACKAGE))
#define GM_IS_MCP_PACKAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
GM_TYPE_MCP_PACKAGE))
#define GM_MCP_PACKAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
GM_TYPE_MCP_PACKAGE, GmMcpPackageClass))
#define GM_MCP_PACKAGE_SESSION(obj) (GM_MCP_SESSION( \
gm_mcp_package_get_session(GM_MCP_PACKAGE(obj))))
/* Private structure type */
typedef struct _GmMcpPackagePrivate GmMcpPackagePrivate;
/*
* Main object structure
*/
typedef struct _GmMcpPackage GmMcpPackage;
struct _GmMcpPackage {
GObject parent;
/*< private > */
GmMcpPackagePrivate *priv;
};
/*
* Class definition
*/
typedef struct _GmMcpPackageClass GmMcpPackageClass;
struct _GmMcpPackageClass {
GObjectClass parent_class;
gchar *name;
gdouble min_version;
gdouble max_version;
GList *depends;
GList *overrides;
void (* handle_simple)(GmMcpPackage *package, gchar *suffix, GList *fields);
gboolean (* handle_multi)(GmMcpPackage *package, gchar *data_tag,
gchar *key, gchar *value, GList *allValues);
/* Signals
void (* proto) (GmMcpPackage *obj); */
};
/* Public */
GType gm_mcp_package_get_type(void) G_GNUC_CONST;
GmMcpPackage *gm_mcp_package_new();
void gm_mcp_package_set_session(GmMcpPackage *package, GObject *session);
void gm_mcp_package_handle_simple(GmMcpPackage *package, gchar *suffix,
GList *fields);
gboolean gm_mcp_package_can_handle_simple(GmMcpPackage *package);
gboolean gm_mcp_package_handle_multi(GmMcpPackage *package, gchar *data_tag,
gchar *key, gchar *value, GList *allValues);
gboolean gm_mcp_package_can_handle_multi(GmMcpPackage *package);
void gm_mcp_package_set_version(GmMcpPackage *package, gdouble version);
gdouble gm_mcp_package_get_version(GmMcpPackage *package);
GObject *gm_mcp_package_get_session(GmMcpPackage *package);
/* Class getters */
gchar const *gm_mcp_package_get_name(GmMcpPackage *package);
GList const *gm_mcp_package_get_depends(GmMcpPackage *package);
GList const *gm_mcp_package_get_overrides(GmMcpPackage *package);
gdouble gm_mcp_package_get_min_version(GmMcpPackage *package);
gdouble gm_mcp_package_get_max_version(GmMcpPackage *package);
G_END_DECLS
#endif /* __GM_MCP_PACKAGE_H__ */

View File

@ -0,0 +1,501 @@
#include <glib-object.h>
#include <string.h>
#include "gm-mcp-session.h"
#include "gm-mcp-classes.h"
#include "gm-mcp.h"
#include "../gm-world.h"
#include "../gm-support.h"
#include "../gm-debug.h"
#include "../gm-options.h"
#define GM_MCP_SESSION_GET_PRIVATE(object)( \
G_TYPE_INSTANCE_GET_PRIVATE((object), \
GM_TYPE_MCP_SESSION, GmMcpSessionPrivate))
struct _GmMcpSessionPrivate {
GmWorld *world;
gchar *authkey;
gdouble version;
GList *packages;
GList *multiline;
};
/* Signals
enum {
PROTO
NUM_SIGNALS
};
static guint gm_mcp_session_signals[NUM_SIGNALS] = {0};*/
G_DEFINE_TYPE(GmMcpSession, gm_mcp_session, G_TYPE_OBJECT)
static void
gm_mcp_session_finalize(GObject *object) {
//GmMcpSession *obj = GM_MCP_SESSION(object);
G_OBJECT_CLASS(gm_mcp_session_parent_class)->finalize(object);
}
static void
gm_mcp_session_class_init(GmMcpSessionClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = gm_mcp_session_finalize;
klass->available_packages = gm_mcp_classes_initialize();
/*gm_mcp_session_signals[PROTO] =
g_signal_new("proto",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmMcpSessionClass, proto),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);*/
g_type_class_add_private(object_class, sizeof(GmMcpSessionPrivate));
}
static void
gm_mcp_session_init(GmMcpSession *obj) {
obj->priv = GM_MCP_SESSION_GET_PRIVATE(obj);
obj->priv->packages = NULL;
obj->priv->version = -1;
obj->priv->authkey = NULL;
obj->priv->multiline = NULL;
}
void
gm_mcp_session_handle_open(GmMcpSession *session, gchar *line) {
GList *fields = gm_mcp_process_key_values(line);
GmMcpPackage *p;
GmMcpPackageClass *pklass;
gchar const *min_version = gm_mcp_find_value(fields, "version");
gchar const *max_version = gm_mcp_find_value(fields, "to");
//gchar *minc, *maxc;
gdouble min_v, max_v, version;
if (!(min_version && max_version)) {
gm_debug_msg(DEBUG_MCP, "GmMcpSession.HandleOpen: invalid opening, "
"min version or max_version not found!");
return;
}
//minc = gm_fix_decimal_point(g_strdup(min_version), strlen(min_version));
//maxc = gm_fix_decimal_point(g_strdup(max_version), strlen(max_version));
min_v = g_ascii_strtod(min_version, NULL);
max_v = g_ascii_strtod(max_version, NULL);
//g_free(minc);
//g_free(maxc);
gm_debug_msg(DEBUG_MCP, "MinMaxVersion: %fd %fd", min_v, max_v);
version = gm_mcp_get_version(MCP_MIN_VERSION, MCP_MAX_VERSION,
min_v, max_v);
if (version > 0.0) {
session->priv->authkey = gm_mcp_generate_auth_key();
session->priv->version = version;
gm_mcp_session_send_simple(session, "mcp", "authentication-key",
session->priv->authkey, "version", "2.1", "to", "2.1", NULL);
pklass = gm_mcp_session_find_package_class("mcp-negotiate");
p = gm_mcp_session_create_package(session, pklass,
pklass->max_version);
} else {
gm_debug_msg(DEBUG_MCP, "GmMcpSession.HandleOpen: mcp server version "
"does not match client version!");
}
}
void
gm_mcp_session_invoke_multiline_handle(GmMcpSession *session, gchar *data_tag,
gchar *key, gchar *value) {
GList *elem;
GmMcpPackage *p;
McpMultilineInfo *minfo;
gboolean handle_all = FALSE;
// Now find the data_tag and emit the handle_multiline
for (elem = session->priv->multiline; elem; elem = elem->next) {
minfo = (McpMultilineInfo *)(elem->data);
if (strcmp(minfo->data_tag, data_tag) == 0) {
p = minfo->package;
if (gm_mcp_package_can_handle_multi(p)) {
if (key) {
if (!gm_mcp_package_handle_multi(p, data_tag, key, value,
NULL)) {
// Not handled a single value, store it for later
minfo->data = g_list_append(minfo->data,
g_strdup(value));
}
} else {
handle_all = gm_mcp_package_handle_multi(p, data_tag, NULL,
NULL, minfo->data);
}
}
if (!key) {
// This is the end, remove the minfo
g_free(minfo->data_tag);
g_free(minfo->key);
// Lines should only be freed when allValues is not handled!
if (!handle_all) {
g_list_free_simple(minfo->data);
} else {
g_list_free(minfo->data);
}
session->priv->multiline = g_list_remove(
session->priv->multiline, minfo);
g_free(minfo);
}
return;
}
}
}
void
gm_mcp_session_handle_multiline(GmMcpSession *session, gchar *line) {
gchar *data_tag, *key, *value, *ptr = line, *key_start;
gm_string_skip_nonspace(&ptr);
if (*ptr == '\0') {
return;
}
data_tag = g_strndup(line, ptr - line);
gm_string_skip_space(&ptr);
key_start = ptr;
gm_string_skip_nonspace(&ptr);
if (*ptr) {
return;
}
key = g_strndup(key_start, (ptr - key_start) - 1);
value = g_strdup(ptr + 1);
gm_mcp_session_invoke_multiline_handle(session, data_tag, key, value);
g_free(data_tag);
g_free(key);
g_free(value);
}
void
gm_mcp_session_handle_multiline_end(GmMcpSession *session, gchar *line) {
gchar *ptr = line;
gchar *data_tag;
while (*ptr != '\0' && g_unichar_isspace(g_utf8_get_char(ptr))) {
ptr = g_utf8_next_char(ptr);
}
data_tag = g_strndup(line, ptr - line);
gm_mcp_session_invoke_multiline_handle(session, data_tag, NULL, NULL);
g_free(data_tag);
}
/* Public */
GmMcpSession *
gm_mcp_session_new(GObject *world) {
GmMcpSession *obj = GM_MCP_SESSION(g_object_new(GM_TYPE_MCP_SESSION, NULL));
obj->priv->world = GM_WORLD(world);
return obj;
}
GList const *
gm_mcp_session_get_packages(GmMcpSession *session) {
return session->priv->packages;
}
GmMcpPackageClass *
gm_mcp_session_package_class_for_each(GmPackageClassFunc func,
gpointer user_data) {
GmMcpSessionClass *klass = g_type_class_peek(GM_TYPE_MCP_SESSION);
GList *item;
GmMcpPackageClass *package_klass;
for (item = klass->available_packages; item; item = item->next) {
package_klass = GM_MCP_PACKAGE_CLASS(item->data);
if (func(package_klass, user_data)) {
return package_klass;
}
}
return NULL;
}
void
gm_mcp_session_send_multiline(GmMcpSession *session, gchar const *data_tag,
gchar const *key, GList const *lines) {
gchar *msg = g_strconcat("#$#* ", data_tag, " ", key, ": ", NULL);
gchar *msg_line = NULL;
while (lines) {
msg_line = g_strconcat(msg, (gchar *)(lines->data), NULL);
gm_world_sendln(session->priv->world, msg_line);
gm_world_log(session->priv->world, LOG_MCP_OUT, msg_line);
g_free(msg_line);
lines = lines->next;
}
g_free(msg);
msg_line = g_strconcat("#$#: ", data_tag, NULL);
gm_world_sendln(session->priv->world, msg_line);
gm_world_log(session->priv->world, LOG_MCP_OUT, msg_line);
g_free(msg_line);
}
void
gm_mcp_session_send_simple(GmMcpSession *session, gchar const *pname,
gchar const *first_key, ...) {
GString *msg = NULL;
va_list args;
gchar const *key, *value;
gchar *esvalue;
msg = g_string_new("#$#");
msg = g_string_append(msg, pname);
if (strcasecmp(pname, "mcp") != 0) {
msg = g_string_append(msg, " ");
msg = g_string_append(msg, session->priv->authkey);
}
msg = g_string_append(msg, " ");
if (first_key != NULL) {
va_start(args, first_key);
key = first_key;
while (key) {
value = va_arg(args, gchar *);
if (value) {
msg = g_string_append(msg, key);
msg = g_string_append(msg, ": ");
esvalue = gm_mcp_escape_if_needed(value);
msg = g_string_append(msg, esvalue);
g_free(esvalue);
msg = g_string_append(msg, " ");
key = va_arg(args, gchar *);
} else {
key = NULL;
}
}
va_end(args);
}
gm_world_sendln(session->priv->world, msg->str);
gm_world_log(session->priv->world, LOG_MCP_OUT, msg->str);
g_string_free(msg, TRUE);
}
GmMcpPackage *
gm_mcp_session_has_package_of_class(GmMcpSession *session,
GmMcpPackageClass *klass) {
GList *item;
GmMcpPackage *package;
GType klass_type = G_TYPE_FROM_CLASS(klass);
for (item = session->priv->packages; item; item = item->next) {
package = (GmMcpPackage *)(item->data);
if (G_TYPE_FROM_CLASS(GM_MCP_PACKAGE_GET_CLASS(package)) ==
klass_type) {
return package;
}
}
return NULL;
}
GmMcpPackage *
gm_mcp_session_find_package(GmMcpSession *session, gchar const *pname) {
GList *elem;
GmMcpPackage *package;
GmMcpPackageClass *package_klass;
for (elem = session->priv->packages; elem; elem = elem->next) {
package = (GmMcpPackage *) (elem->data);
package_klass = GM_MCP_PACKAGE_GET_CLASS(package);
if (strncasecmp(pname, package_klass->name,
strlen(package_klass->name)) == 0 && (strlen(pname) ==
strlen(package_klass->name) ||
pname[strlen(package_klass->name)] == '-')) {
return package;
}
}
return NULL;
}
GmMcpPackage *
gm_mcp_session_create_package(GmMcpSession *session, GmMcpPackageClass *klass,
gdouble version) {
GmMcpPackage *package;
if (!klass) {
return NULL;
}
if (!gm_mcp_session_has_package_of_class(session, klass)) {
package = g_object_new(G_TYPE_FROM_CLASS(G_OBJECT_CLASS(klass)), NULL);
gm_mcp_package_set_session(package, G_OBJECT(session));
gm_mcp_package_set_version(package, version);
session->priv->packages = g_list_append(session->priv->packages,
package);
return package;
} else {
gm_debug_msg(DEBUG_MCP, "GmMcpSession.CreatePackage: package (%s) is "
"already registered for %s", klass->name,
gm_options_get(gm_world_options(session->priv->world), "name"));
return NULL;
}
}
gboolean
gm_mcp_session_find_package_class_real(GmMcpPackageClass *klass,
gpointer user_data) {
gchar *name = (gchar *)(user_data);
return strcmp(name, klass->name) == 0;
}
GmMcpPackageClass *
gm_mcp_session_find_package_class(gchar const *name) {
return gm_mcp_session_package_class_for_each(
&gm_mcp_session_find_package_class_real, (gpointer)(name));
}
void
gm_mcp_session_handle_oob(GmMcpSession *session, gchar *line) {
McpMessageInfo info;
GmMcpPackage *package;
McpMultilineInfo *minfo;
gchar *suffix;
gchar const *value, *full;
gm_debug_msg(DEBUG_MCP, "GmMcpSession.HandleOob: %s", line);
info.fields = NULL;
info.authkey = NULL;
info.name = NULL;
if (strncmp(line, "mcp ", 4) == 0 && session->priv->version == -1) {
gm_mcp_session_handle_open(session, line + 3);
} else if (session->priv->version == -1) {
// No session, drop
gm_debug_msg(DEBUG_MCP, "GmMcpSession.HandleOob: No session found for %s, "
"ignoring out of bound command!", gm_options_get(
gm_world_options(session->priv->world), "name"));
} else if (strncmp(line, "* ", 2) == 0) {
// Multiline
gm_mcp_session_handle_multiline(session, line + 2);
} else if (strncmp(line, ": ", 2) == 0) {
// Multiline end
gm_mcp_session_handle_multiline_end(session, line + 2);
} else {
if (gm_mcp_parse_line(line, &info)) {
// Line is valid mcp
if (strcmp(info.authkey, session->priv->authkey) == 0) {
// Valid authentication
if ((package = gm_mcp_session_find_package(session,
info.name))) {
// Find package
if ((value = gm_mcp_find_multiline_tag(info.fields))) {
// This is multiline start!
if (gm_mcp_find_value(info.fields,
"_data-tag")) {
// This package requests a multiline opening!
minfo = g_new(McpMultilineInfo, 1);
minfo->key = g_strdup(value);
minfo->data_tag = g_strdup(
gm_mcp_find_value(info.fields,
"_data-tag"));
minfo->data = NULL;
minfo->package = package;
session->priv->multiline = g_list_append(
session->priv->multiline, minfo);
gm_debug_msg(DEBUG_MCP, "GmMcpSession.HandleOob: multiline "
"opening detected and stored "
"(data_tag = %s, key = %s)",
minfo->data_tag,
minfo->key);
} else {
gm_debug_msg(DEBUG_MCP, "GmMcpSession.HandleOob: multiline "
"opening detected, but no _data-tag specified, "
"ignore message");
}
}
if (gm_mcp_package_can_handle_simple(package)) {
full = gm_mcp_package_get_name(package);
if (strlen(full) < strlen(info.name)) {
suffix = info.name + (strlen(full) + 1);
} else {
suffix = NULL;
}
gm_mcp_package_handle_simple(package, suffix,
info.fields);
} else {
gm_debug_msg(DEBUG_MCP, "GmMcpSession.HandleOob: can't handle simple message!");
}
} else {
gm_debug_msg(DEBUG_MCP, "GmMcpSession.HandleOob: unsupported package "
"%s, ignore message", info.name);
}
} else {
gm_debug_msg(DEBUG_MCP, "GmMcpSession.HandleOob: invalid authentication "
"key %s instead of %s, ignore message", info.authkey,
session->priv->authkey);
}
} else {
gm_debug_msg(DEBUG_MCP, "GmMcpSession.HandleOob: invalid message "
"(could not parse), ignore message");
}
// Free the info
if (info.authkey) {
g_free(info.authkey);
}
if (info.name) {
g_free(info.name);
}
if (info.fields) {
gm_mcp_destroy_fields(info.fields);
}
}
}
GObject *gm_mcp_session_world(GmMcpSession *session) {
return G_OBJECT(session->priv->world);
}

View File

@ -0,0 +1,89 @@
#ifndef __GM_MCP_SESSION_H__
#define __GM_MCP_SESSION_H__
#include <glib-object.h>
#include "gm-mcp-package.h"
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_MCP_SESSION (gm_mcp_session_get_type())
#define GM_MCP_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
GM_TYPE_MCP_SESSION, GmMcpSession))
#define GM_MCP_SESSION_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
GM_TYPE_MCP_SESSION, GmMcpSession const))
#define GM_MCP_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
GM_TYPE_MCP_SESSION, GmMcpSessionClass))
#define GM_IS_MCP_SESSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
GM_TYPE_MCP_SESSION))
#define GM_IS_MCP_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
GM_TYPE_MCP_SESSION))
#define GM_MCP_SESSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
GM_TYPE_MCP_SESSION, GmMcpSessionClass))
#define GM_MCP_SESSION_WORLD(obj) (GM_WORLD(gm_mcp_session_world(obj)))
typedef struct _McpMultilineInfo {
gchar *key;
gchar *data_tag;
GList *data;
GmMcpPackage *package;
} McpMultilineInfo;
typedef gboolean (* GmPackageClassFunc) (GmMcpPackageClass *klass,
gpointer user_data);
/* Private structure type */
typedef struct _GmMcpSessionPrivate GmMcpSessionPrivate;
/*
* Main object structure
*/
typedef struct _GmMcpSession GmMcpSession;
struct _GmMcpSession {
GObject parent;
/*< private > */
GmMcpSessionPrivate *priv;
};
/*
* Class definition
*/
typedef struct _GmMcpSessionClass GmMcpSessionClass;
struct _GmMcpSessionClass {
GObjectClass parent_class;
GList *available_packages;
/* Signals
void (* proto) (GmMcpSession *obj); */
};
GType gm_mcp_session_get_type(void) G_GNUC_CONST;
GmMcpSession *gm_mcp_session_new(GObject *world);
GObject *gm_mcp_session_world(GmMcpSession *session);
GList const *gm_mcp_session_get_packages(GmMcpSession *session);
void gm_mcp_session_handle_oob(GmMcpSession *session, gchar *line);
GmMcpPackage *gm_mcp_session_has_package_of_class(GmMcpSession *session,
GmMcpPackageClass *klass);
GmMcpPackageClass *gm_mcp_session_find_package_class(gchar const *name);
GmMcpPackage *gm_mcp_session_find_package(GmMcpSession *session,
gchar const *pname);
GmMcpPackageClass *gm_mcp_session_package_class_for_each(
GmPackageClassFunc func, gpointer user_data);
GmMcpPackage *gm_mcp_session_create_package(GmMcpSession *session,
GmMcpPackageClass *klass, gdouble version);
void gm_mcp_session_send_simple(GmMcpSession *session, gchar const *pname,
gchar const *first_key, ...);
void gm_mcp_session_send_multiline(GmMcpSession *session, gchar const *data_tag,
gchar const *key, GList const *lines);
G_END_DECLS
#endif /* __GM_MCP_SESSION_H__ */

263
gnoemoe/mcp/gm-mcp.c Normal file
View File

@ -0,0 +1,263 @@
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "gm-mcp.h"
#include "../gm-support.h"
gdouble
gm_mcp_get_version(gdouble client_min, gdouble client_max, gdouble server_min,
gdouble server_max) {
if (client_max >= server_min && server_max >= client_min) {
if (client_max < server_max) {
return server_max;
} else {
return client_max;
}
} else {
return 0.0;
}
}
gchar const *
gm_mcp_find_value(GList const *fields, gchar const *key) {
GmKeyValuePair *tmp;
while (fields) {
tmp = (GmKeyValuePair *)(fields->data);
if (strcasecmp(key, tmp->key) == 0) {
return tmp->value;
}
fields = fields->next;
}
return NULL;
}
gchar *
gm_mcp_escape_if_needed(gchar const *line) {
GString *new_line;
gchar const *ptr = line;
gchar *result;
if (*line == '\0') {
return g_strdup("\"\"");
}
if (g_utf8_strchr(line, -1, ' ') || g_utf8_strchr(line, -1, '"') ||
g_utf8_strchr(line, -1, '\\')) {
new_line = g_string_new("\"");
while (*ptr != '\0') {
if (*ptr == '"' || *ptr == '\\') {
new_line = g_string_append_c(new_line, '\\');
}
new_line = g_string_append_c(new_line, *ptr);
++ptr;
}
new_line = g_string_append_c(new_line, '"');
result = new_line->str;
g_string_free(new_line, FALSE);
return result;
} else {
return g_strdup(line);
}
}
gchar *
gm_mcp_un_escape(gchar *line) {
gchar *ptri = line, *ptrj = line;
gboolean shifted = FALSE;
while (*ptri != '\0') {
if (*ptri == '\\') {
shifted = TRUE;
++ptri;
}
if (shifted) {
*ptrj = *ptri;
}
++ptrj;
++ptri;
}
*ptrj = '\0';
return line;
}
gchar *
gm_mcp_process_keyval(GList **info, gchar *line) {
gchar *keystart;
gchar *valuestart;
int keylen, valuelen;
GmKeyValuePair *newKeyValue;
if (*line != ' ' || line[1] == '\0' || line[1] == ' ') {
return NULL;
}
keystart = ++line;
gm_string_skip_till(&line, ": ");
if (*line != ':') {
return NULL;
}
keylen = line - keystart;
line = g_utf8_next_char(line);
if (line[1] == '\0' || *line != ' ') {
return NULL;
}
line = g_utf8_next_char(line);
if (*line == ' ') {
return NULL;
}
valuestart = line;
if (*line == '"') {
++valuestart;
++line;
while (*line != '\0' && *line != '"') {
if (*line == '\\') {
++line;
}
++line;
}
valuelen = line - valuestart;
if (*line != '"') {
--valuelen;
} else {
++line;
}
} else {
gm_string_skip_nonspace(&line);
valuelen = line - valuestart;
}
newKeyValue = g_new(GmKeyValuePair, 1);
newKeyValue->key = g_strndup(keystart, keylen);
newKeyValue->value = gm_mcp_un_escape(g_strndup(valuestart, valuelen));
*info = g_list_append(*info, newKeyValue);
return line;
}
GList *
gm_mcp_process_key_values(gchar *line) {
GList *result = NULL;
while (line && *line != '\0') {
line = gm_mcp_process_keyval(&result, line);
}
return result;
}
gboolean
gm_mcp_parse_line(gchar *line, McpMessageInfo *info) {
gchar *p = g_utf8_strchr(line, -1, ' ');
if (!p || p == line) {
return FALSE;
}
info->name = g_strndup(line, (p - line));
line = p + 1;
// Now there should be a authentication key!
p = g_utf8_strchr(line, -1, ' ');
if (!p) {
p = line + strlen(line);
}
info->authkey = g_strndup(line, (p - line));
// Now process the keyvals
info->fields = gm_mcp_process_key_values(p);
return TRUE;
}
gchar *
gm_mcp_generate_key(gint len) {
gchar *s = g_malloc(len + 1);
gchar ref[] =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
gint n = strlen(ref);
gint i;
srand((int) time(NULL));
for (i = 0; i < len; i++) {
s[i] = ref[rand() % n];
}
s[i] = 0;
return s;
}
gchar *
gm_mcp_generate_data_tag() {
return gm_mcp_generate_key(10);
}
gchar *
gm_mcp_generate_auth_key() {
return gm_mcp_generate_key(6);
}
gchar const *
gm_mcp_find_multiline_tag(GList const *fields) {
GmKeyValuePair *data;
gunichar last;
while (fields) {
data = (GmKeyValuePair *)(fields->data);
last = g_utf8_get_char(g_utf8_prev_char(data->key + strlen(data->key)));
if (last == '*') {
return data->key;
}
fields = fields->next;
}
return NULL;
}
void
gm_mcp_destroy_fields(GList *fields) {
GmKeyValuePair *tmp;
GList *elem = fields;
while (elem) {
tmp = (GmKeyValuePair *) (elem->data);
g_free(tmp->key);
g_free(tmp->value);
g_free(elem->data);
elem->data = NULL;
elem = elem->next;
}
g_list_free(fields);
}

28
gnoemoe/mcp/gm-mcp.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef __GM_MCP_H__
#define __GM_MCP_H__
#include <glib.h>
#include "gm-mcp.h"
#define MCP_MIN_VERSION 2.1
#define MCP_MAX_VERSION 2.1
typedef struct _McpMessageInfo {
gchar *name;
gchar *authkey;
GList *fields;
} McpMessageInfo;
gdouble gm_mcp_get_version(gdouble client_min, gdouble client_max,
gdouble server_min, gdouble server_max);
gchar const *gm_mcp_find_value(GList const *fields, gchar const *key);
gchar *gm_mcp_escape_if_needed(gchar const *line);
gchar *gm_mcp_un_escape(gchar *line);
GList *gm_mcp_process_key_values(gchar *line);
gboolean gm_mcp_parse_line(gchar *line, McpMessageInfo *info);
gchar *gm_mcp_generate_data_tag();
gchar *gm_mcp_generate_auth_key();
gchar const *gm_mcp_find_multiline_tag(GList const *fields);
void gm_mcp_destroy_fields(GList *fields);
#endif /* __GM_MCP_H__ */

34
gnoemoe/mcp/mcpinit.rb Executable file
View File

@ -0,0 +1,34 @@
#!/usr/bin/ruby
begin
defs = File.readlines(ARGV[0])
defs.collect! {|elem| elem.chomp}
fout = File.open(ARGV[1] + '.c', 'w')
includes = ['<glib-object.h>']
content = "GList *\ngm_mcp_classes_initialize() {\n\tGList *result = NULL;\n\n"
defs.each do |line|
ucase = line.gsub(/[A-Z]+[a-z]+/) do |s|
s.upcase + '_'
end
ucase.chop!
mcase = ucase.downcase.gsub('_', '-')
includes << '"gm-mcp-' + mcase + '.h"'
content += "\tresult = g_list_append(result, \n\t\t\tg_type_class_ref(GM_TYPE_MCP_" + ucase + "));\n"
end
includes.each {|inc| fout.write('#include ' + inc + "\n")}
fout.write("\n" + content + "\treturn result;\n}\n")
fout.close
fout = File.open(ARGV[1] + '.h', 'w')
fout.write("GList *gm_mcp_classes_initialize();\n")
fout.close
rescue StandardError => boom
p boom
exit(1)
end

View File

@ -0,0 +1,2 @@
Negotiate
AwnsStatus

59
gnoemoe/template.c Normal file
View File

@ -0,0 +1,59 @@
#include <glib-object.h>
#include "gm-{template-}.h"
#define GM_{TEMPLATE}_GET_PRIVATE(object)( \
G_TYPE_INSTANCE_GET_PRIVATE((object), \
GM_TYPE_{TEMPLATE}, Gm{Template}Private))
struct _Gm{Template}Private {
};
/* Signals
enum {
PROTO
NUM_SIGNALS
};
static guint gm_{template_}_signals[NUM_SIGNALS] = {0};*/
G_DEFINE_TYPE(Gm{Template}, gm_{template_}, G_TYPE_OBJECT)
static void
gm_{template_}_finalize(GObject *object) {
//Gm{Template} *obj = GM_{TEMPLATE}(object);
G_OBJECT_CLASS(gm_{template_}_parent_class)->finalize(object);
}
static void
gm_{template_}_class_init(Gm{Template}Class *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = gm_{template_}_finalize;
/*gm_{template_}_signals[PROTO] =
g_signal_new("proto",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(Gm{Template}Class, proto),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);*/
g_type_class_add_private(object_class, sizeof(Gm{Template}Private));
}
static void
gm_{template_}_init(Gm{Template} *obj) {
obj->priv = GM_{TEMPLATE}_GET_PRIVATE(obj);
}
Gm{Template} *
gm_{template_}_new() {
Gm{Template} *obj = GM_{TEMPLATE}(g_object_new(GM_TYPE_{TEMPLATE}, NULL));
return obj;
}

56
gnoemoe/template.h Normal file
View File

@ -0,0 +1,56 @@
#ifndef __GM_{TEMPLATE}_H__
#define __GM_{TEMPLATE}_H__
#include <glib-object.h>
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_{TEMPLATE} (gm_{template_}_get_type())
#define GM_{TEMPLATE}(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
GM_TYPE_{TEMPLATE}, Gm{Template}))
#define GM_{TEMPLATE}_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
GM_TYPE_{TEMPLATE}, Gm{Template} const))
#define GM_{TEMPLATE}_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
GM_TYPE_{TEMPLATE}, Gm{Template}Class))
#define GM_IS_{TEMPLATE}(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
GM_TYPE_{TEMPLATE}))
#define GM_IS_{TEMPLATE}_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
GM_TYPE_{TEMPLATE}))
#define GM_{TEMPLATE}_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
GM_TYPE_{TEMPLATE}, Gm{Template}Class))
/* Private structure type */
typedef struct _Gm{Template}Private Gm{Template}Private;
/*
* Main object structure
*/
typedef struct _Gm{Template} Gm{Template};
struct _Gm{Template} {
GObject parent;
/*< private > */
Gm{Template}Private *priv;
};
/*
* Class definition
*/
typedef struct _Gm{Template}Class Gm{Template}Class;
struct _Gm{Template}Class {
GObjectClass parent_class;
/* Signals
void (* proto) (Gm{Template} *obj); */
};
GType gm_{template_}_get_type(void) G_GNUC_CONST;
Gm{Template} *gm_{template_}_new(void);
G_END_DECLS
#endif /* __GM_{TEMPLATE}_H__ */

View File

@ -0,0 +1,10 @@
## Process this file with automake to produce Makefile.in
widgetsdir = widgets
gnoemoe_SOURCES += \
$(widgetsdir)/gm-app-view.c $(widgetsdir)/gm-app-view.h \
$(widgetsdir)/gm-world-view.c $(widgetsdir)/gm-world-view.h \
$(widgetsdir)/gm-world-text-view.c $(widgetsdir)/gm-world-text-view.h \
$(widgetsdir)/gm-world-input-view.c $(widgetsdir)/gm-world-input-view.h \
$(widgetsdir)/gm-world-tab.c $(widgetsdir)/gm-world-tab.h \
$(widgetsdir)/gm-text-scroller.c $(widgetsdir)/gm-text-scroller.h

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,78 @@
#ifndef __GM_APP_VIEW_H__
#define __GM_APP_VIEW_H__
#include <gtk/gtk.h>
#include <glib.h>
#include <glade/glade.h>
#include "../gm-world.h"
#include "../gm-app.h"
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_APP_VIEW (gm_app_view_get_type())
#define GM_APP_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_APP_VIEW, GmAppView))
#define GM_APP_VIEW_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_APP_VIEW, GmAppView const))
#define GM_APP_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_APP_VIEW, GmAppViewClass))
#define GM_IS_APP_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_APP_VIEW))
#define GM_IS_APP_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_APP_VIEW))
#define GM_APP_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_APP_VIEW, GmAppViewClass))
/* Private structure type */
typedef struct _GmAppViewPrivate GmAppViewPrivate;
/*
* Main object structure
*/
typedef struct _GmAppView GmAppView;
struct _GmAppView {
GtkWindow window;
/*< private > */
GmAppViewPrivate *priv;
};
/*
* Class definition
*/
typedef struct _GmAppViewClass GmAppViewClass;
struct _GmAppViewClass {
GtkWindowClass parent_class;
/* Signals */
};
GType gm_app_view_get_type(void) G_GNUC_CONST;
GmAppView *gm_app_view_new(GmApp *application);
const GmApp *gm_app_view_application(GmAppView *view);
/* Callbacks */
void on_gm_app_view_world_new(GtkMenuItem * menuitem, GmAppView *view);
void on_gm_app_view_world_connect(GtkMenuItem * menuitem, GmAppView *view);
void on_gm_app_view_world_logs(GtkMenuItem * menuitem, GmAppView *view);
void on_gm_app_view_world_info(GtkMenuItem * menuitem, GmAppView *view);
void on_gm_app_view_world_close(GtkMenuItem * menuitem, GmAppView *view);
void on_gm_app_view_world_quit(GtkMenuItem * menuitem, GmAppView *view);
void on_gm_app_view_edit_cut(GtkMenuItem * menuitem, GmAppView *view);
void on_gm_app_view_edit_copy(GtkMenuItem * menuitem, GmAppView *view);
void on_gm_app_view_edit_paste(GtkMenuItem * menuitem, GmAppView *view);
void on_gm_app_view_edit_worlds(GtkMenuItem * menuitem, GmAppView *view);
void on_gm_app_view_edit_world(GtkMenuItem * menuitem, GmAppView *view);
void on_gm_app_view_edit_find(GtkMenuItem * menuitem, GmAppView *view);
void on_gm_app_view_edit_find_next(GtkMenuItem * menuitem, GmAppView *view);
void on_gm_app_view_edit_preferences(
GtkMenuItem * menuitem, GmAppView *view);
void on_gm_app_view_view_mcp(GtkMenuItem * menuitem, GmAppView *view);
void on_gm_app_view_view_scripts(GtkMenuItem * menuitem, GmAppView *view);
void on_gm_app_view_help_about(GtkMenuItem * menuitem, GmAppView *view);
G_END_DECLS
#endif /* __GM_APP_VIEW__ */

View File

@ -0,0 +1,259 @@
#include <glib-object.h>
#include <gtk/gtk.h>
#include <string.h>
#include "gm-text-scroller.h"
#include "../gm-debug.h"
#include "../gm-support.h"
#define GM_TEXT_SCROLLER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_TEXT_SCROLLER, GmTextScrollerPrivate))
struct _GmTextScrollerPrivate {
GtkTextView *text_view;
GtkTextBuffer *text_buffer;
GtkScrolledWindow *scrolled_window;
gint character_height;
gboolean end_scrolled;
gint idle_handler;
};
void on_gm_text_scroller_text_view_style_set(GtkTextView *view,
GtkStyle *previous_style, GmTextScroller *scroller);
void on_gm_text_scroller_text_buffer_changed(GtkTextBuffer *text_buffer,
GmTextScroller *scroller);
void on_gm_text_scroller_text_view_notify(GtkTextView *text_view, GParamSpec *arg1,
GmTextScroller *scroller);
void on_gm_text_scroller_text_view_destroy(GtkTextView *text_view,
GmTextScroller *scroller);
/* Signals
enum {
PROTO
NUM_SIGNALS
};
static guint gm_text_scroller_signals[NUM_SIGNALS] = {0};*/
G_DEFINE_TYPE(GmTextScroller, gm_text_scroller, G_TYPE_OBJECT)
static void
gm_text_scroller_finalize(GObject *object) {
GmTextScroller *obj = GM_TEXT_SCROLLER(object);
if (obj->priv->text_buffer != NULL) {
g_signal_handlers_disconnect_by_func(obj->priv->text_buffer,
G_CALLBACK(on_gm_text_scroller_text_buffer_changed),
obj);
g_object_unref(obj->priv->text_buffer);
}
g_signal_handlers_disconnect_by_func(obj->priv->text_view,
G_CALLBACK(on_gm_text_scroller_text_view_notify), obj);
g_signal_handlers_disconnect_by_func(obj->priv->text_view,
G_CALLBACK(on_gm_text_scroller_text_view_style_set), obj);
g_signal_handlers_disconnect_by_func(obj->priv->text_view,
G_CALLBACK(on_gm_text_scroller_text_view_destroy), obj);
G_OBJECT_CLASS(gm_text_scroller_parent_class)->finalize(object);
}
static void
gm_text_scroller_class_init(GmTextScrollerClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = gm_text_scroller_finalize;
/*gm_text_scroller_signals[PROTO] =
g_signal_new("proto",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmTextScrollerClass, proto),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);*/
g_type_class_add_private(object_class, sizeof(GmTextScrollerPrivate));
}
static void
gm_text_scroller_init(GmTextScroller *obj) {
obj->priv = GM_TEXT_SCROLLER_GET_PRIVATE(obj);
obj->priv->character_height = 10;
obj->priv->end_scrolled = FALSE;
}
void
gm_text_scroller_update_text_buffer(GmTextScroller *scroller) {
if (scroller->priv->text_buffer != NULL) {
g_signal_handlers_disconnect_by_func(scroller->priv->text_buffer,
G_CALLBACK(on_gm_text_scroller_text_buffer_changed),
scroller);
g_object_unref(scroller->priv->text_buffer);
}
scroller->priv->text_buffer =
gtk_text_view_get_buffer(scroller->priv->text_view);
if (scroller->priv->text_buffer != NULL) {
g_object_ref(scroller->priv->text_buffer);
g_signal_connect(scroller->priv->text_buffer, "changed",
G_CALLBACK(on_gm_text_scroller_text_buffer_changed), scroller);
}
}
void
gm_text_scroller_scroll_end(GmTextScroller *scroller) {
GtkTextMark *mark;
GtkTextIter iter;
mark = gtk_text_buffer_get_mark(scroller->priv->text_buffer,
"end-of-buffer");
if (mark == NULL) {
gtk_text_buffer_get_end_iter(scroller->priv->text_buffer, &iter);
mark = gtk_text_buffer_create_mark(scroller->priv->text_buffer,
"end-of-buffer", &iter, FALSE);
}
gtk_text_view_scroll_to_mark(scroller->priv->text_view, mark, 0.0,
TRUE, 1.0, 1.0);
}
void
gm_text_scroller_scroll_begin(GmTextScroller *scroller) {
GtkTextMark *mark;
GtkTextIter iter;
mark = gtk_text_buffer_get_mark(scroller->priv->text_buffer,
"begin-of-buffer");
if (mark == NULL) {
gtk_text_buffer_get_start_iter(scroller->priv->text_buffer, &iter);
mark = gtk_text_buffer_create_mark(scroller->priv->text_buffer,
"begin-of-buffer", &iter, TRUE);
}
gtk_text_view_scroll_to_mark(scroller->priv->text_view, mark, 0.0,
TRUE, 0.0, 0.0);
}
gboolean
gm_text_scroller_scroll_end_idle(GmTextScroller *scroller) {
scroller->priv->idle_handler = 0;
gm_text_scroller_scroll_end(scroller);
scroller->priv->end_scrolled = FALSE;
return FALSE;
}
void
gm_text_scroller_prepare(GmTextScroller *scroller) {
GtkAdjustment *ad = gtk_scrolled_window_get_vadjustment(
scroller->priv->scrolled_window);
scroller->priv->end_scrolled = scroller->priv->end_scrolled ||
((ad->page_size + ad->value) >= ad->upper -
(double)(scroller->priv->character_height));
if (scroller->priv->idle_handler == 0 && scroller->priv->end_scrolled) {
scroller->priv->idle_handler = g_idle_add((GSourceFunc)
gm_text_scroller_scroll_end_idle, scroller);
}
}
void
gm_text_scroller_update_character_height(GmTextScroller *scroller) {
GtkRcStyle *style = gtk_widget_get_modifier_style(GTK_WIDGET(
scroller->priv->text_view));
PangoContext *pc = gtk_widget_create_pango_context(GTK_WIDGET(
scroller->priv->text_view));
PangoLayout *pl;
gint cwidth, cheight;
if (style->font_desc != NULL) {
pango_context_set_font_description(pc, style->font_desc);
pl = pango_layout_new(pc);
pango_layout_set_text(pl, "G", 1);
pango_layout_get_pixel_size(pl, &(cwidth),
&(cheight));
if (scroller->priv->character_height != cheight) {
scroller->priv->character_height = cheight;
}
g_object_unref(pl);
}
g_object_unref(pc);
}
GmTextScroller *
gm_text_scroller_new(GtkTextView *text_view) {
GmTextScroller *obj;
GtkWidget *parent = gtk_widget_get_parent(GTK_WIDGET(text_view));
if (parent == NULL || !GTK_IS_SCROLLED_WINDOW(parent)) {
return NULL;
}
obj = GM_TEXT_SCROLLER(g_object_new(GM_TYPE_TEXT_SCROLLER,
NULL));
obj->priv->text_view = text_view;
obj->priv->scrolled_window = GTK_SCROLLED_WINDOW(parent);
gm_text_scroller_update_text_buffer(obj);
gm_text_scroller_update_character_height(obj);
g_signal_connect(text_view, "notify",
G_CALLBACK(on_gm_text_scroller_text_view_notify), obj);
g_signal_connect(text_view, "style-set",
G_CALLBACK(on_gm_text_scroller_text_view_style_set), obj);
g_signal_connect(text_view, "destroy",
G_CALLBACK(on_gm_text_scroller_text_view_destroy), obj);
obj->priv->idle_handler = g_idle_add((GSourceFunc)
gm_text_scroller_scroll_end_idle, obj);
return obj;
}
// Callbacks
void
on_gm_text_scroller_text_view_notify(GtkTextView *text_view, GParamSpec *arg1,
GmTextScroller *scroller) {
if (strcmp(arg1->name, "buffer") == 0) {
// Buffer changed
gm_text_scroller_update_text_buffer(scroller);
}
}
void
on_gm_text_scroller_text_view_style_set(GtkTextView *view,
GtkStyle *previous_style, GmTextScroller *scroller) {
gm_text_scroller_update_character_height(scroller);
}
void
on_gm_text_scroller_text_buffer_changed(GtkTextBuffer *text_buffer,
GmTextScroller *scroller) {
// Changed...
gm_text_scroller_prepare(scroller);
}
void
on_gm_text_scroller_text_view_destroy(GtkTextView *text_view,
GmTextScroller *scroller) {
// Remove ourselfs when the text view dies
g_object_unref(scroller);
}

View File

@ -0,0 +1,52 @@
#ifndef __GM_TEXT_SCROLLER_H__
#define __GM_TEXT_SCROLLER_H__
#include <glib-object.h>
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_TEXT_SCROLLER (gm_text_scroller_get_type())
#define GM_TEXT_SCROLLER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_TEXT_SCROLLER, GmTextScroller))
#define GM_TEXT_SCROLLER_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_TEXT_SCROLLER, GmTextScroller const))
#define GM_TEXT_SCROLLER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_TEXT_SCROLLER, GmTextScrollerClass))
#define GM_IS_TEXT_SCROLLER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_TEXT_SCROLLER))
#define GM_IS_TEXT_SCROLLER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_TEXT_SCROLLER))
#define GM_TEXT_SCROLLER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_TEXT_SCROLLER, GmTextScrollerClass))
/* Private structure type */
typedef struct _GmTextScrollerPrivate GmTextScrollerPrivate;
/*
* Main object structure
*/
typedef struct _GmTextScroller GmTextScroller;
struct _GmTextScroller {
GObject parent;
/*< private > */
GmTextScrollerPrivate *priv;
};
/*
* Class definition
*/
typedef struct _GmTextScrollerClass GmTextScrollerClass;
struct _GmTextScrollerClass {
GObjectClass parent_class;
/* Signals
void (* proto) (GmTextScroller *obj); */
};
GType gm_text_scroller_get_type(void) G_GNUC_CONST;
GmTextScroller *gm_text_scroller_new(GtkTextView *text_view);
void gm_text_scroller_scroll_end(GmTextScroller *scroller);
void gm_text_scroller_scroll_begin(GmTextScroller *scroller);
G_END_DECLS
#endif /* __GM_TEXT_SCROLLER_H__ */

View File

@ -0,0 +1,392 @@
#include <string.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#include "gm-world-input-view.h"
#include "gm-world-view.h"
#include "../gm-world.h"
#include "../gm-color-table.h"
#include "../gm-debug.h"
#define GM_WORLD_INPUT_VIEW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_WORLD_INPUT_VIEW, GmWorldInputViewPrivate))
void on_gm_world_input_view_color_table_color_changed(GmColorTable *table,
gchar *name, GmWorldInputView *view);
void on_gm_world_input_view_color_table_font_changed(GmColorTable *table,
gchar *font_description, GmWorldInputView *view);
gboolean on_gm_world_input_view_key_pressed(GtkWidget *widget,
GdkEventKey * event, gpointer userdata);
gboolean on_gm_world_input_view_key_released(GtkWidget *widget,
GdkEventKey *event, gpointer userdata);
struct _GmWorldInputViewPrivate {
GmColorTable *color_table;
GList **history;
GList *position;
gchar *prefix;
};
/* Signals */
enum {
TEXT_ACTIVATE,
NUM_SIGNALS
};
static guint world_input_view_signals[NUM_SIGNALS] = {0};
G_DEFINE_TYPE(GmWorldInputView, gm_world_input_view, GTK_TYPE_TEXT_VIEW)
/* Private functions */
static void
gm_world_input_view_finalize(GObject *object) {
GmWorldInputView *view = GM_WORLD_INPUT_VIEW(object);
gm_world_input_view_set_color_table(view, NULL);
G_OBJECT_CLASS(gm_world_input_view_parent_class)->finalize(object);
}
static void
gm_world_input_view_class_init(GmWorldInputViewClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = gm_world_input_view_finalize;
world_input_view_signals[TEXT_ACTIVATE] =
g_signal_new("text_activate",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmWorldInputViewClass, text_activate),
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE,
1,
G_TYPE_STRING);
g_type_class_add_private(object_class, sizeof(GmWorldInputViewPrivate));
}
static void
gm_world_input_view_init(GmWorldInputView *view) {
view->priv = GM_WORLD_INPUT_VIEW_GET_PRIVATE(view);
view->priv->color_table = NULL;
view->priv->history = NULL;
view->priv->position = NULL;
view->priv->prefix = NULL;
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD_CHAR);
gtk_text_view_set_accepts_tab(GTK_TEXT_VIEW(view), TRUE);
gtk_text_view_set_left_margin(GTK_TEXT_VIEW(view), 3);
gtk_text_view_set_right_margin(GTK_TEXT_VIEW(view), 3);
gtk_text_view_set_pixels_above_lines(GTK_TEXT_VIEW(view), 1);
gtk_text_view_set_pixels_below_lines(GTK_TEXT_VIEW(view), 1);
g_signal_connect(view, "key_press_event",
G_CALLBACK(on_gm_world_input_view_key_pressed), NULL);
g_signal_connect(view, "key_release_event",
G_CALLBACK(on_gm_world_input_view_key_released), NULL);
}
gchar *
gm_world_input_view_str_new_value(gchar *old, gchar *new) {
g_free(old);
if (new) {
return g_strdup(new);
} else {
return NULL;
}
}
void
gm_world_input_view_set_text(GmWorldInputView *view, gchar *text, gint len) {
GtkTextIter end;
GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
gtk_text_buffer_set_text(buffer, text, len);
gtk_text_buffer_get_end_iter(buffer, &end);
gtk_text_buffer_place_cursor(buffer, &end);
}
void
gm_world_input_view_update_colors(GmWorldInputView *view) {
/*GdkColor col;
if (view->priv->color_table != NULL) {
if (gm_color_table_get(view->priv->color_table, "bg_default", &col)) {
gtk_widget_modify_base(GTK_WIDGET(view), GTK_STATE_NORMAL,
&col);
}
if (gm_color_table_get(view->priv->color_table, "fg_default", &col)) {
gtk_widget_modify_text(GTK_WIDGET(view), GTK_STATE_NORMAL,
&col);
}
}*/
}
void
gm_world_input_view_update_font(GmWorldInputView *view) {
PangoFontDescription *f = pango_font_description_from_string(
gm_color_table_font_description(view->priv->color_table));
if (f != NULL) {
gtk_widget_modify_font(GTK_WIDGET(view), f);
pango_font_description_free(f);
}
}
/* Public functions */
GtkWidget *
gm_world_input_view_new() {
GtkWidget *result;
GmColorTable *table = gm_color_table_new();
result = gm_world_input_view_new_with_color_table(table);
g_object_unref(table);
return result;
}
GtkWidget *
gm_world_input_view_new_with_color_table(GmColorTable *color_table) {
GmWorldInputView *view = GM_WORLD_INPUT_VIEW(
g_object_new(GM_TYPE_WORLD_INPUT_VIEW, NULL));
gm_world_input_view_set_color_table(view, color_table);
return GTK_WIDGET(view);
}
void
gm_world_input_view_set_history(GmWorldInputView *view, GList **history) {
view->priv->history = history;
view->priv->position = NULL;
view->priv->prefix = NULL;
}
GList **
gm_world_input_view_history(GmWorldInputView *view) {
return view->priv->history;
}
void
gm_world_input_view_set_color_table(GmWorldInputView *view,
GmColorTable *color_table) {
if (view->priv->color_table != NULL) {
g_signal_handlers_disconnect_by_func(view->priv->color_table,
on_gm_world_input_view_color_table_color_changed, view);
g_signal_handlers_disconnect_by_func(view->priv->color_table,
on_gm_world_input_view_color_table_font_changed, view);
g_object_unref(view->priv->color_table);
}
if (color_table != NULL) {
view->priv->color_table = g_object_ref(color_table);
g_signal_connect(view->priv->color_table, "color_changed",
G_CALLBACK(on_gm_world_input_view_color_table_color_changed), view);
g_signal_connect(view->priv->color_table, "font_changed",
G_CALLBACK(on_gm_world_input_view_color_table_font_changed), view);
gm_world_input_view_update_colors(view);
gm_world_input_view_update_font(view);
} else {
view->priv->color_table = NULL;
}
}
GmColorTable *
gm_world_input_view_color_table(GmWorldInputView *view) {
return view->priv->color_table;
}
/* Callbacks */
void
on_gm_world_input_view_color_table_color_changed(GmColorTable *table,
gchar *name, GmWorldInputView *view) {
if (strcmp(name, "fg_default") == 0 || strcmp(name, "bg_default") == 0) {
gm_world_input_view_update_colors(view);
}
}
void
on_gm_world_input_view_color_table_font_changed(GmColorTable *table,
gchar *font_description, GmWorldInputView *view) {
gm_world_input_view_update_font(view);
}
gboolean
on_gm_world_input_view_key_released(GtkWidget *widget, GdkEventKey *event,
gpointer userdata) {
GmWorldInputView *view = GM_WORLD_INPUT_VIEW(widget);
GtkTextBuffer *buffer;
GtkTextIter start, end;
gchar *text;
buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
gtk_text_buffer_get_bounds(buffer, &start, &end);
text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
switch (event->keyval) {
case GDK_Delete: case GDK_BackSpace:
if (text[0] == '\0') {
// Reset position to the last item in the history
view->priv->position = g_list_last(*view->priv->history);
// Reset prefix to NULL
view->priv->prefix = gm_world_input_view_str_new_value(
view->priv->prefix, NULL);
}
break;
}
g_free(text);
return FALSE;
}
gboolean
on_gm_world_input_view_key_pressed(GtkWidget *widget, GdkEventKey *event,
gpointer userdata) {
GmWorldInputView *view = GM_WORLD_INPUT_VIEW(widget);
gchar *text;
gboolean result = TRUE, isUp;
GtkTextBuffer *buf;
GtkTextIter start, end, cursor;
GtkTextMark *insert;
GList *item, *found = NULL;
gint line;
buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
gtk_text_buffer_get_bounds(buf, &start, &end);
text = gtk_text_buffer_get_text(buf, &start, &end, FALSE);
switch (event->keyval) {
case GDK_Up: case GDK_Down:
isUp = event->keyval == GDK_Up;
if (!view->priv->history) {
break;
}
insert = gtk_text_buffer_get_insert(buf);
gtk_text_buffer_get_iter_at_mark(buf, &cursor, insert);
line = gtk_text_iter_get_line(&cursor);
if ((isUp && line != 0) || (!isUp && line !=
gtk_text_buffer_get_line_count(buf) - 1)) {
g_free(text);
return FALSE;
}
// If the current position is empty then append a new history item
if (!view->priv->position) {
*view->priv->history = g_list_append(*view->priv->history,
g_strdup(text));
view->priv->position = g_list_last(*view->priv->history);
}
// If there is nowhere to move, don't even bother
if ((isUp && !view->priv->position->prev)
|| (!isUp && !view->priv->position->next)) {
break;
}
// If the current prefix is NULL then we set the new prefix to
// the current text
if (view->priv->prefix == NULL) {
view->priv->prefix = g_strdup(text);
}
// If the prefix is an empty string then simply advance to the
// next item
if (view->priv->prefix[0] == '\0') {
if (isUp) {
found = view->priv->position->prev;
} else {
found = view->priv->position->next;
}
} else {
// Else find the closest matching history line
item = isUp ? view->priv->position->prev :
view->priv->position->next;
while (item) {
if (strncmp((gchar *)item->data, view->priv->prefix,
strlen(view->priv->prefix)) == 0) {
// Change current position to the matched item
found = item;
break;
}
item = isUp ? item->prev : item->next;
}
}
// If a match is found then set this history text
if (found) {
// Change the data of the current position to the text
// now in the buffer.
view->priv->position->data =
gm_world_input_view_str_new_value(
view->priv->position->data, text);
gm_world_input_view_set_text(view, (gchar *)found->data, -1);
view->priv->position = found;
if (found == g_list_last(*view->priv->history)) {
view->priv->prefix = gm_world_input_view_str_new_value(
view->priv->prefix, NULL);
}
}
break;
case GDK_Return:
// Emit the text_activate signal
if (!(event->state & GDK_CONTROL_MASK) &&
!(event->state & GDK_SHIFT_MASK)) {
g_signal_emit(view, world_input_view_signals[TEXT_ACTIVATE], 0,
text);
if (view->priv->history) {
item = g_list_last(*view->priv->history);
if (item) {
item->data = gm_world_input_view_str_new_value(
(gchar *)(item->data), text);
} else {
*view->priv->history =
g_list_append(*view->priv->history,
g_strdup(text));
}
}
// TODO: manage history length
// Append new empty history item which will become our new
// current item
if (view->priv->history) {
*view->priv->history =
g_list_append(*view->priv->history, g_strdup(""));
view->priv->position = g_list_last(*view->priv->history);
}
// Reset prefix to NULL
view->priv->prefix = gm_world_input_view_str_new_value(
view->priv->prefix, NULL);
// Set textview text to an empty string
gm_world_input_view_set_text(view, "", 0);
} else {
result = FALSE;
}
break;
default:
result = FALSE;
break;
}
g_free(text);
return result;
}

View File

@ -0,0 +1,57 @@
#ifndef __GM_WORLD_INPUT_VIEW_H__
#define __GM_WORLD_INPUT_VIEW_H__
#include <gtk/gtk.h>
#include "../gm-color-table.h"
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_WORLD_INPUT_VIEW (gm_world_input_view_get_type())
#define GM_WORLD_INPUT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD_INPUT_VIEW, GmWorldInputView))
#define GM_WORLD_INPUT_VIEW_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD_INPUT_VIEW, GmWorldInputView const))
#define GM_WORLD_INPUT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_WORLD_INPUT_VIEW, GmWorldInputViewClass))
#define GM_IS_WORLD_INPUT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_WORLD_INPUT_VIEW))
#define GM_IS_WORLD_INPUT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_WORLD_INPUT_VIEW))
#define GM_WORLD_INPUT_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_WORLD_INPUT_VIEW, GmWorldInputViewClass))
/* Private structure type */
typedef struct _GmWorldInputViewPrivate GmWorldInputViewPrivate;
/*
* Main object structure
*/
typedef struct _GmWorldInputView GmWorldInputView;
struct _GmWorldInputView {
GtkTextView textview;
/*< private > */
GmWorldInputViewPrivate *priv;
};
/*
* Class definition
*/
typedef struct _GmWorldInputViewClass GmWorldInputViewClass;
struct _GmWorldInputViewClass {
GtkTextViewClass parent_class;
/* Signals */
void (* text_activate) (GmWorldInputView *view, const gchar *text);
};
GType gm_world_input_view_get_type(void) G_GNUC_CONST;
GtkWidget *gm_world_input_view_new();
GtkWidget *gm_world_input_view_new_with_color_table(GmColorTable *table);
void gm_world_input_view_set_color_table(GmWorldInputView *view,
GmColorTable *color_table);
GmColorTable *gm_world_input_view_color_table(GmWorldInputView *view);
void gm_world_input_view_set_history(GmWorldInputView *view, GList **history);
GList **gm_world_input_view_history(GmWorldInputView *view);
G_END_DECLS
#endif /* __GM_WORLD_INPUT_VIEW_H__ */

View File

@ -0,0 +1,196 @@
#include <gtk/gtk.h>
#include "../gm-world.h"
#include "gm-world-tab.h"
#include "../gm-support.h"
#include "../gm-pixbuf.h"
#include "../gm-debug.h"
#define GM_WORLD_TAB_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_WORLD_TAB, GmWorldTabPrivate))
void on_gm_world_tab_button_clicked(GtkButton *button, GmWorldTab *tab);
void on_gm_world_tab_world_activity_changed(GmWorld *world, gint activity,
GmWorldTab *tab);
void on_gm_world_tab_world_name_changed(GmWorld *world, gchar *text,
GmWorldTab *tab);
struct _GmWorldTabPrivate {
GmWorld *world;
GtkImage *image;
GtkLabel *label;
GtkButton *button;
guint source_id;
};
// Signals
enum {
CLOSE,
NUM_SIGNALS
};
static guint gm_world_tab_signals[NUM_SIGNALS] = {0};
G_DEFINE_TYPE(GmWorldTab, gm_world_tab, GTK_TYPE_HBOX)
static void
gm_world_tab_stop_recolor(GmWorldTab *tab) {
if (tab->priv->source_id != 0) {
g_source_remove(tab->priv->source_id);
tab->priv->source_id = 0;
}
}
static void
gm_world_tab_finalize(GObject *object) {
GmWorldTab *obj = GM_WORLD_TAB(object);
gm_world_tab_stop_recolor(obj);
if (obj->priv->world) {
g_signal_handlers_disconnect_by_func(obj->priv->world,
G_CALLBACK(on_gm_world_tab_world_activity_changed), obj);
g_signal_handlers_disconnect_by_func(obj->priv->world,
G_CALLBACK(on_gm_world_tab_world_name_changed), obj);
g_object_unref(obj->priv->world);
}
G_OBJECT_CLASS(gm_world_tab_parent_class)->finalize(object);
}
static void
gm_world_tab_class_init(GmWorldTabClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = gm_world_tab_finalize;
gm_world_tab_signals[CLOSE] =
g_signal_new("close",
G_OBJECT_CLASS_TYPE(object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GmWorldTabClass, close),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
g_type_class_add_private(object_class, sizeof(GmWorldTabPrivate));
}
static void
gm_world_tab_create_interface(GmWorldTab *obj) {
GtkWidget *image, *label, *button, *image_button;
image = gtk_image_new();
label = gtk_label_new("");
button = gtk_button_new();
image_button = gtk_image_new_from_stock(GTK_STOCK_CLOSE,
GTK_ICON_SIZE_BUTTON);
gtk_widget_set_size_request(button, 16, 16);
gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
gtk_container_add(GTK_CONTAINER(button), image_button);
gtk_box_pack_start(GTK_BOX(obj), image, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(obj), label, FALSE, FALSE, 0);
gtk_box_pack_end(GTK_BOX(obj), button, FALSE, FALSE, 0);
gtk_box_set_spacing(GTK_BOX(obj), 6);
obj->priv->image = GTK_IMAGE(image);
obj->priv->label = GTK_LABEL(label);
obj->priv->button = GTK_BUTTON(button);
}
static void
gm_world_tab_init(GmWorldTab *obj) {
obj->priv = GM_WORLD_TAB_GET_PRIVATE(obj);
obj->priv->source_id = 0;
obj->priv->world = NULL;
gm_world_tab_create_interface(obj);
g_signal_connect(obj->priv->button, "clicked",
G_CALLBACK(on_gm_world_tab_button_clicked), obj);
gtk_widget_show_all(GTK_WIDGET(obj));
gtk_widget_hide(GTK_WIDGET(obj));
}
gboolean
gm_world_tab_update_color(GmWorldTab *tab) {
gchar *text;
gint activity = gm_world_activity(tab->priv->world);
text = g_strdup_printf("<span color=\"#0000%02x\">%s [%i]</span>",
CALC_COLOR_RANGE(activity),
gm_world_name(tab->priv->world), activity);
gtk_label_set_markup(tab->priv->label, text);
g_free(text);
tab->priv->source_id = 0;
return FALSE;
}
static void
gm_world_tab_update(GmWorldTab *tab) {
gint activity = gm_world_activity(tab->priv->world);
gchar *text;
gm_world_tab_stop_recolor(tab);
if (activity) {
text = g_strdup_printf("<span color=\"#%02x0000\">%s [%i]</span>",
CALC_COLOR_RANGE(activity),
gm_world_name(tab->priv->world), activity);
gtk_image_set_from_pixbuf(tab->priv->image,
gm_pixbuf_get_at_size("world_activity.svg", 16, 16));
tab->priv->source_id = g_idle_add((GSourceFunc)
gm_world_tab_update_color, tab);
} else {
text = g_strdup(gm_world_name(tab->priv->world));
gtk_image_set_from_pixbuf(tab->priv->image,
gm_pixbuf_get_at_size("world.svg", 16, 16));
}
gtk_label_set_markup(tab->priv->label, text);
g_free(text);
}
// Public
GmWorldTab *
gm_world_tab_new(GmWorld *world) {
GmWorldTab *obj = GM_WORLD_TAB(g_object_new(GM_TYPE_WORLD_TAB, NULL));
obj->priv->world = g_object_ref(world);
g_signal_connect(world, "activity_changed",
G_CALLBACK(on_gm_world_tab_world_activity_changed), obj);
g_signal_connect(world, "name_changed",
G_CALLBACK(on_gm_world_tab_world_name_changed), obj);
gm_world_tab_update(obj);
return obj;
}
// Callbacks
void
on_gm_world_tab_button_clicked(GtkButton *button, GmWorldTab *tab) {
g_signal_emit(tab, gm_world_tab_signals[CLOSE], 0);
}
void
on_gm_world_tab_world_activity_changed(GmWorld *world, gint activity,
GmWorldTab *tab) {
gm_world_tab_update(tab);
}
void
on_gm_world_tab_world_name_changed(GmWorld *world, gchar *text,
GmWorldTab *tab) {
gm_world_tab_update(tab);
}

View File

@ -0,0 +1,50 @@
#ifndef __GM_WORLD_TAB_H__
#define __GM_WORLD_TAB_H__
#include <gtk/gtk.h>
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_WORLD_TAB (gm_world_tab_get_type())
#define GM_WORLD_TAB(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD_TAB, GmWorldTab))
#define GM_WORLD_TAB_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD_TAB, GmWorldTab const))
#define GM_WORLD_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_WORLD_TAB, GmWorldTabClass))
#define GM_IS_WORLD_TAB(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_WORLD_TAB))
#define GM_IS_WORLD_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_WORLD_TAB))
#define GM_WORLD_TAB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_WORLD_TAB, GmWorldTabClass))
/* Private structure type */
typedef struct _GmWorldTabPrivate GmWorldTabPrivate;
/*
* Main object structure
*/
typedef struct _GmWorldTab GmWorldTab;
struct _GmWorldTab {
GtkHBox parent;
/*< private > */
GmWorldTabPrivate *priv;
};
/*
* Class definition
*/
typedef struct _GmWorldTabClass GmWorldTabClass;
struct _GmWorldTabClass {
GtkHBoxClass parent_class;
// Signals
void (* close) (GmWorldTab *obj);
};
GType gm_world_tab_get_type(void) G_GNUC_CONST;
GmWorldTab *gm_world_tab_new(GmWorld *world);
G_END_DECLS
#endif /* __GM_WORLD_TAB_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,59 @@
#ifndef __GM_WORLD_TEXT_VIEW_H__
#define __GM_WORLD_TEXT_VIEW_H__
#include <gtk/gtk.h>
#include "../gm-color-table.h"
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_WORLD_TEXT_VIEW (gm_world_text_view_get_type())
#define GM_WORLD_TEXT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD_TEXT_VIEW, GmWorldTextView))
#define GM_WORLD_TEXT_VIEW_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD_TEXT_VIEW, GmWorldTextView const))
#define GM_WORLD_TEXT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_WORLD_TEXT_VIEW, GmWorldTextViewClass))
#define GM_IS_WORLD_TEXT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_WORLD_TEXT_VIEW))
#define GM_IS_WORLD_TEXT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_WORLD_TEXT_VIEW))
#define GM_WORLD_TEXT_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_WORLD_TEXT_VIEW, GmWorldTextViewClass))
/* Private structure type */
typedef struct _GmWorldTextViewPrivate GmWorldTextViewPrivate;
/*
* Main object structure
*/
typedef struct _GmWorldTextView GmWorldTextView;
struct _GmWorldTextView {
GtkTextView textview;
/*< private > */
GmWorldTextViewPrivate *priv;
};
/*
* Class definition
*/
typedef struct _GmWorldTextViewClass GmWorldTextViewClass;
struct _GmWorldTextViewClass {
GtkTextViewClass parent_class;
/* Signals */
void (* url_activate) (GmWorldTextView *view, const gchar *url);
void (* character_size_changed) (GmWorldTextView *view, gint width,
gint height);
};
GType gm_world_text_view_get_type(void) G_GNUC_CONST;
GtkWidget *gm_world_text_view_new(void);
GtkWidget *gm_world_text_view_new_with_color_table(GmColorTable *color_table);
gchar *gm_world_text_view_insert(GmWorldTextView *view, const gchar *text);
GmColorTable *gm_world_text_view_color_table(GmWorldTextView *view);
void gm_world_text_view_set_color_table(GmWorldTextView *view,
GmColorTable *color_table);
G_END_DECLS
#endif /* __GM_WORLD_TEXT_VIEW_H__ */

View File

@ -0,0 +1,567 @@
#include <gdk/gdkkeysyms.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-debug.h"
#include "../gm-color-table.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;
GtkHPaned *hpaned;
GtkTreeView *tree_view_userlist;
GtkTreeModel *tree_model_userlist;
GmWorldTextView *text_view_world;
GmWorldInputView *text_view_input;
GmTextScroller *text_scroller_world;
};
enum {
GM_WORLD_VIEW_USERLIST_ICON,
GM_WORLD_VIEW_USERLIST_NAME,
GM_WORLD_VIEW_USERLIST_OBJ,
GM_WORLD_VIEW_USERLIST_SORT,
GM_WORLD_VIEW_USERLIST_N_COLUMNS
};
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, gboolean active,
GmWorldView *view);
void on_gm_world_view_world_status_changed(GmWorld *world, gchar const *text,
GmWorldView *view);
gboolean on_gm_world_view_world_text_view_scroll_event(GmWorldView *view,
GdkEventScroll *event, GmWorldTextView *text);
gboolean on_gm_world_view_world_input_view_key_pressed(GtkWidget *widget,
GdkEventKey *event, GmWorldView *view);
/* Signals */
/*enum {
NUM_SIGNALS
};
static guint world_view_signals[NUM_SIGNALS] = {0};*/
G_DEFINE_TYPE(GmWorldView, gm_world_view, GTK_TYPE_NOTEBOOK)
static void
gm_world_view_finalize(GObject *object) {
GmWorldView *view = GM_WORLD_VIEW(object);
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_status_changed), view);
g_object_unref(view->priv->world);
G_OBJECT_CLASS(gm_world_view_parent_class)->finalize(object);
}
static void
gm_world_view_class_init(GmWorldViewClass *klass) {
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = gm_world_view_finalize;
g_type_class_add_private(object_class, sizeof(GmWorldViewPrivate));
}
GtkTreeModel *
gm_world_view_userlist_model_new(GmWorldView *view) {
GtkListStore *store =
gtk_list_store_new(GM_WORLD_VIEW_USERLIST_N_COLUMNS, GDK_TYPE_PIXBUF,
G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
GtkTreeModel *model = gtk_tree_model_sort_new_with_model(
GTK_TREE_MODEL(store));
view->priv->tree_model_userlist = model;
return model;
}
GtkWidget *
gm_world_view_userlist_new(GmWorldView *view) {
GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
GtkTreeModel *model = gm_world_view_userlist_model_new(view);
GtkWidget *tree_view = gtk_tree_view_new_with_model(model);
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
GTK_SHADOW_IN);
gtk_widget_set_size_request(scrolled_window, 150, -1);
gtk_widget_set_size_request(tree_view, 150, -1);
gtk_container_add(GTK_CONTAINER(scrolled_window), tree_view);
renderer = gtk_cell_renderer_pixbuf_new();
column = gtk_tree_view_column_new_with_attributes(_("I"), renderer, "pixbuf",
GM_WORLD_VIEW_USERLIST_ICON, NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
gtk_tree_view_column_set_min_width(column, 30);
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer,
"text", GM_WORLD_VIEW_USERLIST_NAME, NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model),
GM_WORLD_VIEW_USERLIST_SORT, GTK_SORT_ASCENDING);
GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(tree_view), GTK_CAN_FOCUS);
gtk_tree_view_columns_autosize(GTK_TREE_VIEW(tree_view));
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
view->priv->tree_view_userlist = GTK_TREE_VIEW(tree_view);
return scrolled_window;
}
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);
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);
}
}
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_paned_pack2(GTK_PANED(hpaned), gm_world_view_userlist_new(view),
FALSE, 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 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);
}
void
on_gm_world_view_destroy(GmWorldView *view, gpointer user_data) {
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));
}
void
on_gm_world_view_show(GmWorldView *view, gpointer user_data) {
gm_do_events();
gtk_paned_set_position(GTK_PANED(view->priv->hpaned),
GTK_WIDGET(view->priv->hpaned)->allocation.width
- gm_options_get_int(gm_world_options(view->priv->world),
"pane_position"));
}
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, "active_changed",
G_CALLBACK(on_gm_world_view_world_active_changed), view);
g_signal_connect(world, "status_changed",
G_CALLBACK(on_gm_world_view_world_status_changed), view);
g_signal_connect(view->priv->text_view_input, "text_activate",
G_CALLBACK(on_gm_world_input_view_world_text_activate), view);
g_signal_connect(view, "destroy",
G_CALLBACK(on_gm_world_view_destroy), NULL);
g_signal_connect(view, "show",
G_CALLBACK(on_gm_world_view_show), NULL);
return GTK_WIDGET(view);
}
gboolean
gm_world_view_find_first(GmWorldView *view, const gchar *str,
GmWorldViewSearchFlags flags) {
GtkTextView *tview = GTK_TEXT_VIEW(view->priv->text_view_world);
GtkTextIter iter;
GtkTextBuffer *buffer;
if (tview) {
buffer = gtk_text_view_get_buffer(tview);
if (buffer) {
if (flags & GM_WORLD_VIEW_SEARCH_BACKWARDS) {
gtk_text_buffer_get_end_iter(buffer, &iter);
} else {
gtk_text_buffer_get_start_iter(buffer, &iter);
}
gtk_text_buffer_place_cursor(buffer, &iter);
return gm_world_view_find_next(view, str, flags);
}
}
return FALSE;
}
gboolean
gm_world_view_find_next(GmWorldView *view, const gchar *str,
GmWorldViewSearchFlags flags) {
GtkTextBuffer *buffer = NULL;
GtkTextView *tview = NULL;
GtkTextIter end, start, matchStart, matchEnd;
gboolean found = FALSE;
if (*str == '\0') {
return FALSE;
}
tview = GTK_TEXT_VIEW(view->priv->text_view_world);
if (tview) {
buffer = gtk_text_view_get_buffer(tview);
}
if (buffer) {
if (!gtk_text_buffer_get_selection_bounds(buffer, &start, &end)) {
if (flags & GM_WORLD_VIEW_SEARCH_BACKWARDS) {
gtk_text_buffer_get_end_iter(buffer, &end);
} else {
gtk_text_buffer_get_start_iter(buffer, &end);
}
start = end;
}
if (flags & GM_WORLD_VIEW_SEARCH_FORWARDS) {
found = gtk_text_iter_forward_search(&end, str,
GTK_TEXT_SEARCH_VISIBLE_ONLY | GTK_TEXT_SEARCH_TEXT_ONLY,
&matchStart, &matchEnd, NULL);
} else {
found = gtk_text_iter_backward_search(&start, str,
GTK_TEXT_SEARCH_VISIBLE_ONLY | GTK_TEXT_SEARCH_TEXT_ONLY,
&matchStart, &matchEnd, NULL);
}
if (found) {
gtk_text_buffer_place_cursor(buffer, &matchStart);
gtk_text_buffer_move_mark_by_name(buffer, "selection_bound",
&matchEnd);
gtk_text_view_scroll_to_iter(tview, &matchStart,
0.0, FALSE, 0.0, 0.0);
}
}
return found;
}
gboolean
gm_world_view_text_active(GmWorldView *view) {
return gtk_notebook_get_current_page(GTK_NOTEBOOK(view)) == 0;
}
void
gm_world_view_scroll_end_prepare(GmWorldView *view) {
// TODO
}
void
gm_world_view_scroll_end(GmWorldView *view) {
// TODO
}
GmWorld *
gm_world_view_world(GmWorldView *view) {
return view->priv->world;
}
GmWorldInputView *
gm_world_view_input(GmWorldView *view) {
return view->priv->text_view_input;
}
GtkTextBuffer *
gm_world_view_buffer(GmWorldView *view) {
return gtk_text_view_get_buffer(GTK_TEXT_VIEW(view->priv->text_view_world));
}
void
gm_world_view_open_log(GmWorldView *view, const gchar *filename,
OpenLogProgress callback, gpointer user_data) {
}
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_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_writeln(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"));
}
break;
case GM_NET_STATE_DISCONNECTING:
line = g_strdup(_("# Disconnecting"));
break;
default:
break;
}
if (line) {
gm_world_writeln(world, line);
g_free(line);
}
}
void
on_gm_world_view_world_active_changed(GmWorld *world, gboolean active,
GmWorldView *view) {
if (active) {
gtk_widget_grab_focus(GTK_WIDGET(view->priv->text_view_input));
}
}
void
on_gm_world_view_world_status_changed(GmWorld *world, gchar const *text,
GmWorldView *view) {
gm_world_view_update_status(view, text);
}
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;
default:
break;
}
return FALSE;
}

View File

@ -0,0 +1,75 @@
#ifndef __GM_WORLD_VIEW_H__
#define __GM_WORLD_VIEW_H__
#include <gtk/gtk.h>
#include "../gm-support.h"
#include "../gm-world.h"
#include "gm-world-input-view.h"
G_BEGIN_DECLS
/*
* Type checking and casting macros
*/
#define GM_TYPE_WORLD_VIEW (gm_world_view_get_type())
#define GM_WORLD_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD_VIEW, GmWorldView))
#define GM_WORLD_VIEW_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GM_TYPE_WORLD_VIEW, GmWorldView const))
#define GM_WORLD_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GM_TYPE_WORLD_VIEW, GmWorldViewClass))
#define GM_IS_WORLD_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GM_TYPE_WORLD_VIEW))
#define GM_IS_WORLD_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GM_TYPE_WORLD_VIEW))
#define GM_WORLD_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GM_TYPE_WORLD_VIEW, GmWorldViewClass))
/* Private structure type */
typedef struct _GmWorldViewPrivate GmWorldViewPrivate;
/*
* Main object structure
*/
typedef struct _GmWorldView GmWorldView;
struct _GmWorldView {
GtkNotebook notebook;
/*< private > */
GmWorldViewPrivate *priv;
};
/*
* Class definition
*/
typedef struct _GmWorldViewClass GmWorldViewClass;
struct _GmWorldViewClass {
GtkNotebookClass parent_class;
/* Signals */
};
typedef enum _GmWorldViewSearchFlags {
GM_WORLD_VIEW_SEARCH_NONE = 0,
GM_WORLD_VIEW_SEARCH_FORWARDS = 1 << 0,
GM_WORLD_VIEW_SEARCH_BACKWARDS = 1 << 1
} GmWorldViewSearchFlags;
GType gm_world_view_get_type(void) G_GNUC_CONST;
GtkWidget *gm_world_view_new(GmWorld *world);
gboolean gm_world_view_find_first(GmWorldView *view, const gchar *str,
GmWorldViewSearchFlags flags);
gboolean gm_world_view_find_next(GmWorldView *view, const gchar *str,
GmWorldViewSearchFlags flags);
void gm_world_view_scroll_end_prepare(GmWorldView *view);
void gm_world_view_scroll_end(GmWorldView *view);
GmWorld *gm_world_view_world(GmWorldView *view);
GmWorldInputView *gm_world_view_input(GmWorldView *view);
GtkTextBuffer *gm_world_view_buffer(GmWorldView *view);
gboolean gm_world_view_text_active(GmWorldView *view);
void gm_world_view_open_log(GmWorldView *view, const gchar *filename,
OpenLogProgress callback, gpointer user_data);
void gm_world_view_set_userlist_width(GmWorldView *view, gint width);
void gm_world_view_set_focus(GmWorldView *view);
G_END_DECLS
#endif /* __GM_WORLD_VIEW_H__ */