1485 lines
36 KiB
C
1485 lines
36 KiB
C
/*
|
|
* Copyright (C) 2012 - Juan Ferrer Toribio
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "vn-gui.h"
|
|
#include <gdome.h>
|
|
#include <stdlib.h>
|
|
#include <glib/gstdio.h>
|
|
|
|
#include <libxml/tree.h>
|
|
#include <libxml/parser.h>
|
|
#include <libxml/valid.h>
|
|
|
|
#define MAIN_UI _GUI_DIR"/main.glade"
|
|
#define MENU_UI _GUI_DIR"/menu.glade"
|
|
#define MODULE_DTD _DTD_DIR"/module.dtd"
|
|
|
|
#define S(string) (gdome_str_mkref (string))
|
|
#define gtk_builder_get(builder, id) ((gpointer) gtk_builder_get_object (builder, id))
|
|
|
|
/**
|
|
* SECTION: vn-gui
|
|
* @Short_description: GUI manager
|
|
* @Title: VnGui
|
|
*
|
|
* Manages most of the GUI operations.
|
|
**/
|
|
G_DEFINE_TYPE (VnGui, vn_gui, G_TYPE_OBJECT);
|
|
|
|
struct _VnWindow
|
|
{
|
|
VnGui * obj;
|
|
GtkWindow * widget;
|
|
GtkDialog * about;
|
|
GtkHeaderBar * header;
|
|
GtkWidget * menu_button;
|
|
GtkWidget * spinner;
|
|
GtkNotebook * notebook;
|
|
VnForm * active_form;
|
|
guint merge_id;
|
|
gboolean maximized;
|
|
};
|
|
|
|
enum {
|
|
COL_ICON
|
|
,COL_NAME
|
|
,COL_TITLE
|
|
,COL_TYPE
|
|
,COL_MODULE
|
|
,COL_COUNT
|
|
};
|
|
|
|
enum {
|
|
LOGOUT
|
|
,EXIT
|
|
,LAST_SIGNAL
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
VnGui * obj;
|
|
gboolean aux;
|
|
GError * error;
|
|
GThread * thread;
|
|
}
|
|
GuiData;
|
|
|
|
void vn_gui_on_new_window_activated (GSimpleAction * a, GVariant * v, gpointer obj);
|
|
void vn_gui_on_logout_activated (GSimpleAction * a, GVariant * v, gpointer obj);
|
|
void vn_gui_on_open_activated (GSimpleAction * a, GVariant * v, gpointer obj);
|
|
void vn_gui_on_about_activated (GSimpleAction * a, GVariant * v, gpointer obj);
|
|
void vn_gui_on_exit_activated (GSimpleAction * a, GVariant * v, gpointer obj);
|
|
void vn_gui_on_close_tab_activated (GSimpleAction * a, GVariant * v, gpointer obj);
|
|
void vn_gui_on_open_form_activated (GSimpleAction * action, GVariant * v, gpointer obj);
|
|
|
|
static void vn_gui_reconnect (VnGui * obj);
|
|
static void vn_gui_on_conn_error (DbConn * conn, const GError * error, VnGui * obj);
|
|
static void vn_gui_on_conn_status_changed (DbConn * conn, DbConnStatus status, VnGui * obj);
|
|
void vn_gui_on_window_destroyed (GtkWindow * widget, VnWindow * window);
|
|
void vn_gui_on_page_removed (GtkNotebook * notebook, GtkWidget * page, guint num, VnWindow * window);
|
|
|
|
static guint signals[LAST_SIGNAL] = {0};
|
|
|
|
static const GActionEntry app_entries[] =
|
|
{
|
|
{"new-window", vn_gui_on_new_window_activated}
|
|
,{"logout", vn_gui_on_logout_activated}
|
|
,{"connect", vn_gui_on_open_activated}
|
|
,{"about", vn_gui_on_about_activated}
|
|
,{"quit", vn_gui_on_exit_activated}
|
|
};
|
|
|
|
static const GActionEntry win_entries[] =
|
|
{
|
|
{"close", vn_gui_on_close_tab_activated}
|
|
};
|
|
|
|
static const GActionEntry open_action[] =
|
|
{
|
|
{"open-form", vn_gui_on_open_form_activated, "s"}
|
|
};
|
|
|
|
/**
|
|
* vn_gui_new:
|
|
* @app: a #GtkApplication
|
|
* @conn: the #VnLogin
|
|
*
|
|
* Creates a new Gui object.
|
|
*
|
|
* Return value: the created #VnGui
|
|
**/
|
|
VnGui * vn_gui_new (GtkApplication * app, DbConn * conn)
|
|
{
|
|
return g_object_new (VN_TYPE_GUI, "app", app, "conn", conn, NULL);
|
|
}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private
|
|
|
|
/*
|
|
* Frees the #GuiData struct.
|
|
*/
|
|
static void gui_data_free (GuiData * gui_data)
|
|
{
|
|
if (gui_data->error)
|
|
g_error_free (gui_data->error);
|
|
|
|
g_object_unref (gui_data->obj);
|
|
g_thread_unref (gui_data->thread);
|
|
g_free (gui_data);
|
|
}
|
|
|
|
static void vn_gui_free_window (VnGui * obj, VnWindow * window)
|
|
{
|
|
g_signal_handlers_disconnect_by_func (window->widget,
|
|
vn_gui_on_window_destroyed, window);
|
|
g_signal_handlers_disconnect_by_func (window->notebook,
|
|
vn_gui_on_page_removed, window);
|
|
|
|
gtk_widget_destroy (GTK_WIDGET (window->widget));
|
|
g_free (window);
|
|
}
|
|
|
|
/*
|
|
* Validates a module definition file at @path against the DTD
|
|
*/
|
|
static gboolean vn_gui_validate_module_definition (VnGui * obj, const gchar * path)
|
|
{
|
|
gboolean valid = FALSE;
|
|
xmlValidCtxtPtr ctxt = xmlNewValidCtxt ();
|
|
|
|
if (ctxt)
|
|
{
|
|
xmlDocPtr doc = xmlParseFile (path);
|
|
|
|
if (doc)
|
|
{
|
|
xmlDtdPtr dtd = xmlParseDTD (NULL, (const xmlChar *) MODULE_DTD);
|
|
|
|
if (!dtd)
|
|
g_error ("The DTD is not well formed!");
|
|
|
|
valid = xmlValidateDtd (ctxt, doc, dtd);
|
|
xmlFreeDtd (dtd);
|
|
}
|
|
|
|
xmlFreeValidCtxt (ctxt);
|
|
xmlFreeDoc (doc);
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
/*
|
|
* Loads a module and all of its forms.
|
|
*/
|
|
static void vn_gui_load_module (VnGui * obj, const gchar * dir, const gchar * file)
|
|
{
|
|
gint n;
|
|
gchar * mod_title_strip, * mod_name;
|
|
const gchar * text_dom = NULL;
|
|
GModule * module = NULL;
|
|
VnMod * mod = NULL;
|
|
GdomeException e;
|
|
GdomeDocument * doc;
|
|
GdomeDOMImplementation * di;
|
|
GdomeNodeList * nl;
|
|
GdomeElement * el;
|
|
GdomeNode * node;
|
|
GdomeDOMString * mod_title;
|
|
GdomeDOMString * library;
|
|
GdomeDOMString * name_str;
|
|
gchar * path = g_strdup_printf ("%s/%s", dir, file);
|
|
|
|
// Getting the module info from XML file
|
|
|
|
di = gdome_di_mkref ();
|
|
doc = gdome_di_createDocFromURI (di, path, 0, &e);
|
|
gdome_di_unref (di, &e);
|
|
|
|
name_str = S("name");
|
|
|
|
if (doc)
|
|
{
|
|
gboolean is_module = FALSE;
|
|
GdomeDocumentType * dt = gdome_doc_doctype (doc, &e);
|
|
|
|
if (dt)
|
|
{
|
|
GdomeDOMString * dt_name = gdome_dt_name (dt, &e);
|
|
is_module = dt_name && !g_strcmp0 (dt_name->str, "hedera-module");
|
|
gdome_str_unref (dt_name);
|
|
gdome_dt_unref (dt, &e);
|
|
}
|
|
|
|
if (is_module)
|
|
{
|
|
if (vn_gui_validate_module_definition (obj, path))
|
|
{
|
|
GdomeDOMString * library_str = S("library"),
|
|
* form_str = S("form");
|
|
|
|
nl = gdome_doc_getElementsByTagName (doc, library_str, &e);
|
|
el = (GdomeElement *) gdome_nl_item (nl, 0, &e);
|
|
gdome_str_unref (library_str);
|
|
gdome_nl_unref (nl, &e);
|
|
|
|
library = gdome_el_getAttribute (el, name_str, &e);
|
|
mod_name = g_strdup (library->str);
|
|
|
|
node = gdome_el_firstChild (el, &e);
|
|
mod_title = gdome_n_nodeValue (node, &e);
|
|
mod_title_strip = g_strstrip (g_strdup (mod_title->str));
|
|
nl = gdome_doc_getElementsByTagName (doc, form_str, &e);
|
|
|
|
gdome_str_unref (form_str);
|
|
gdome_str_unref (library);
|
|
gdome_doc_unref (doc, &e);
|
|
gdome_n_unref (node, &e);
|
|
gdome_el_unref (el, &e);
|
|
}
|
|
else
|
|
{
|
|
g_warning ("VnGui: The module definition at \"%s\" is not valid", path);
|
|
gdome_doc_unref (doc, &e);
|
|
gdome_str_unref (name_str);
|
|
g_free (path);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gdome_doc_unref (doc, &e);
|
|
gdome_str_unref (name_str);
|
|
g_free (path);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_warning ("VnGui: Error loading module info file: %s", (gchar *) file);
|
|
gdome_str_unref (name_str);
|
|
g_free (path);
|
|
return;
|
|
}
|
|
|
|
g_free (path);
|
|
|
|
// Loading the module dynamically
|
|
|
|
for (n = 0; obj->lib_dirs[n] && !module; n++)
|
|
{
|
|
path = g_module_build_path (obj->lib_dirs[n], mod_name);
|
|
module = g_module_open (path, 0);
|
|
g_free (path);
|
|
}
|
|
|
|
if (module)
|
|
{
|
|
GType mod_type = VN_TYPE_MOD;
|
|
VnModGetTypeFunc mod_get_type_func;
|
|
gchar * c_name = g_strdelimit (g_strdup (mod_name), "-. ", '_'),
|
|
* symbol_name = g_strdup_printf ("vn_%s_get_type", c_name);
|
|
|
|
g_module_make_resident (module);
|
|
|
|
if (g_module_symbol (module, symbol_name, (gpointer) &mod_get_type_func)
|
|
&& g_type_is_a (mod_get_type_func (), VN_TYPE_MOD))
|
|
mod_type = mod_get_type_func ();
|
|
|
|
mod = g_object_new (mod_type
|
|
,"name" ,mod_name
|
|
,"data-dir" ,dir
|
|
,"module" ,module
|
|
,"title" ,mod_title_strip
|
|
,"gui" ,obj
|
|
,NULL
|
|
);
|
|
|
|
g_free (c_name);
|
|
g_free (symbol_name);
|
|
}
|
|
else
|
|
g_warning ("VnGui: Can't load module %s: %s", mod_name, g_module_error ());
|
|
|
|
// If successful, load forms
|
|
|
|
if (mod)
|
|
{
|
|
gulong len, n;
|
|
GtkTreeIter parent_iter;
|
|
GdomeDOMString * icon_str = S("icon");
|
|
|
|
// Creating folder to put forms inside
|
|
|
|
text_dom = vn_mod_get_text_domain (mod);
|
|
gtk_tree_store_append (obj->tree, &parent_iter, NULL);
|
|
gtk_tree_store_set (obj->tree, &parent_iter
|
|
,COL_ICON ,"gtk-directory"
|
|
,COL_TITLE ,g_dgettext (text_dom, mod_title_strip)
|
|
,COL_TYPE ,G_TYPE_NONE
|
|
,COL_NAME ,NULL
|
|
,-1
|
|
);
|
|
|
|
len = gdome_nl_length (nl, &e) ;
|
|
|
|
for (n = 0; n < len; n++)
|
|
{
|
|
gchar * c_name, * title_strip, * symbol_name;
|
|
GdomeDOMString * icon, * name, * title;
|
|
VnFormGetTypeFunc form_get_type_func;
|
|
|
|
el = (GdomeElement *) gdome_nl_item (nl, n, &e);
|
|
icon = gdome_el_getAttribute (el, icon_str, &e);
|
|
|
|
name = gdome_el_getAttribute (el, name_str, &e);
|
|
c_name = g_strdelimit (g_strdup (name->str), "-. ", '_');
|
|
|
|
node = gdome_el_firstChild (el, &e);
|
|
title = gdome_n_nodeValue (node, &e);
|
|
title_strip = g_strstrip (g_strdup (g_dgettext (text_dom, title->str)));
|
|
|
|
gdome_n_unref (node, &e);
|
|
gdome_el_unref (el, &e);
|
|
|
|
symbol_name = g_strdup_printf ("vn_%s_get_type", c_name);
|
|
|
|
if (g_module_symbol (module, symbol_name, (gpointer) &form_get_type_func))
|
|
{
|
|
GType type = form_get_type_func ();
|
|
|
|
if (g_type_is_a (type, VN_TYPE_FORM))
|
|
{
|
|
GtkTreeIter * iter = g_new (GtkTreeIter, 1);
|
|
gtk_tree_store_append (obj->tree, iter, &parent_iter);
|
|
gtk_tree_store_set (obj->tree, iter
|
|
,COL_NAME ,name->str
|
|
,COL_ICON ,icon->str
|
|
,COL_TITLE ,title_strip
|
|
,COL_TYPE ,type
|
|
,COL_MODULE ,mod
|
|
,-1
|
|
);
|
|
|
|
g_hash_table_replace (obj->forms, g_strdup (name->str), iter);
|
|
}
|
|
else
|
|
g_warning ("VnGui: %s isn't a VnForm", g_type_name (type));
|
|
}
|
|
else
|
|
g_warning ("VnGui: Error loading form: %s", g_module_error ());
|
|
|
|
g_free (c_name);
|
|
g_free (title_strip);
|
|
g_free (symbol_name);
|
|
gdome_str_unref (name);
|
|
gdome_str_unref (icon);
|
|
gdome_str_unref (title);
|
|
}
|
|
|
|
obj->modules = g_slist_prepend (obj->modules, mod);
|
|
|
|
gdome_str_unref (icon_str);
|
|
}
|
|
|
|
g_free (mod_title_strip);
|
|
g_free (mod_name);
|
|
gdome_str_unref (name_str);
|
|
gdome_str_unref (mod_title);
|
|
gdome_nl_unref (nl, &e);
|
|
}
|
|
|
|
static void set_accelerator (VnGui * obj, GMenuModel * model, gint item, gboolean enable)
|
|
{
|
|
GMenuAttributeIter * iter;
|
|
GVariant * value;
|
|
GVariant * target = NULL;
|
|
const char * key;
|
|
const char * accel = NULL;
|
|
const char * action = NULL;
|
|
|
|
iter = g_menu_model_iterate_item_attributes (model, item);
|
|
|
|
while (g_menu_attribute_iter_get_next (iter, &key, &value))
|
|
{
|
|
if (g_str_equal (key, "action") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
|
|
action = g_variant_get_string (value, NULL);
|
|
else if (g_str_equal (key, "accel") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
|
|
accel = g_variant_get_string (value, NULL);
|
|
else if (g_str_equal (key, "target"))
|
|
target = g_variant_ref (value);
|
|
|
|
g_variant_unref (value);
|
|
}
|
|
|
|
g_object_unref (iter);
|
|
|
|
if (accel && action)
|
|
{
|
|
if (enable)
|
|
gtk_application_add_accelerator (obj->app, accel, action, target);
|
|
else
|
|
gtk_application_remove_accelerator (obj->app, action, target);
|
|
}
|
|
|
|
if (target)
|
|
g_variant_unref (target);
|
|
}
|
|
|
|
static void vn_gui_set_menu_accels (VnGui * obj, GMenuModel * menu, gboolean enable)
|
|
{
|
|
gint i;
|
|
|
|
for (i = 0; i < g_menu_model_get_n_items (menu); i++)
|
|
{
|
|
GMenuLinkIter * iter;
|
|
GMenuModel * more;
|
|
const gchar * key;
|
|
|
|
set_accelerator (obj, menu, i, enable);
|
|
|
|
iter = g_menu_model_iterate_item_links (menu, i);
|
|
|
|
while (g_menu_link_iter_get_next (iter, &key, &more))
|
|
{
|
|
vn_gui_set_menu_accels (obj, more, enable);
|
|
g_object_unref (more);
|
|
}
|
|
|
|
g_object_unref (iter);
|
|
}
|
|
}
|
|
|
|
static VnWindow * vn_gui_add_window (VnGui * obj, GtkWindow * widget, GtkNotebook * notebook)
|
|
{
|
|
GSList * n;
|
|
GtkWidget * button;
|
|
VnWindow * window = g_new (VnWindow, 1);
|
|
|
|
window->obj = obj;
|
|
window->widget = widget;
|
|
window->notebook = notebook;
|
|
window->active_form = NULL;
|
|
window->merge_id = 0;
|
|
|
|
obj->windows = g_slist_prepend (obj->windows, window);
|
|
|
|
gtk_application_add_window (obj->app, widget);
|
|
gtk_notebook_set_group_name (notebook,
|
|
g_application_get_application_id (G_APPLICATION (obj->app)));
|
|
|
|
// Setting header and window menu
|
|
|
|
window->header = g_object_new (GTK_TYPE_HEADER_BAR
|
|
,"show-close-button", TRUE
|
|
,"title", obj->app_title
|
|
,"subtitle", db_conn_get_user (obj->conn)
|
|
,NULL);
|
|
gtk_window_set_titlebar (widget, GTK_WIDGET (window->header));
|
|
|
|
button = gtk_menu_button_new ();
|
|
g_action_map_add_action_entries (G_ACTION_MAP (window->widget),
|
|
win_entries, G_N_ELEMENTS (win_entries), window);
|
|
gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (button), obj->main_menu);
|
|
gtk_button_set_image (GTK_BUTTON (button),
|
|
gtk_image_new_from_icon_name ("emblem-system-symbolic",
|
|
GTK_ICON_SIZE_BUTTON));
|
|
gtk_header_bar_pack_end (window->header, button);
|
|
|
|
button = gtk_menu_button_new ();
|
|
gtk_widget_set_no_show_all (button, TRUE);
|
|
gtk_header_bar_pack_start (window->header, button);
|
|
window->menu_button = button;
|
|
|
|
window->spinner = gtk_spinner_new ();
|
|
gtk_widget_set_opacity (window->spinner, 0);
|
|
gtk_header_bar_pack_end (window->header, window->spinner);
|
|
|
|
// Loading the modules actions
|
|
|
|
g_action_map_add_action_entries (G_ACTION_MAP (window->widget),
|
|
open_action, G_N_ELEMENTS (open_action), window);
|
|
|
|
for (n = obj->modules; n; n = n->next)
|
|
{
|
|
gint size;
|
|
VnMod * mod = VN_MOD (n->data);
|
|
const GActionEntry * actions = vn_mod_get_actions (mod, &size);
|
|
g_action_map_add_action_entries (G_ACTION_MAP (window->widget),
|
|
actions, size, mod);
|
|
}
|
|
|
|
vn_gui_set_menu_accels (obj, obj->main_menu, TRUE);
|
|
|
|
gtk_widget_show_all (GTK_WIDGET (widget));
|
|
return window;
|
|
}
|
|
|
|
/*
|
|
* Shows an error dialog.
|
|
*/
|
|
static void vn_gui_show_error (VnGui * obj, const GError * error)
|
|
{
|
|
GtkWidget * dialog;
|
|
GtkWindow * window = obj->active_window ? obj->active_window->widget : NULL;
|
|
|
|
if (error && error->code == DB_CONN_ERROR_LOST)
|
|
dialog = gtk_message_dialog_new (window
|
|
,GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT
|
|
,GTK_MESSAGE_QUESTION
|
|
,GTK_BUTTONS_YES_NO
|
|
,_("Connection has been lost. Do you want to reconnect?")
|
|
);
|
|
else
|
|
dialog = gtk_message_dialog_new (window
|
|
,GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT
|
|
,GTK_MESSAGE_WARNING
|
|
,GTK_BUTTONS_CLOSE
|
|
,_("An error occurred in the connection.")
|
|
);
|
|
|
|
gtk_window_set_title (GTK_WINDOW (dialog), _("Database error"));
|
|
|
|
if (error)
|
|
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
|
|
"%s", error->message);
|
|
else
|
|
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
|
|
_("Unknown error"));
|
|
|
|
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES)
|
|
vn_gui_reconnect (obj);
|
|
|
|
gtk_widget_destroy (GTK_WIDGET (dialog));
|
|
}
|
|
|
|
/*
|
|
* Closes and saves the GUI interface.
|
|
*/
|
|
static void vn_gui_close (VnGui * obj)
|
|
{
|
|
GSList * n;
|
|
|
|
g_return_if_fail (VN_IS_GUI (obj));
|
|
|
|
if (obj->windows)
|
|
{
|
|
vn_gui_save (obj);
|
|
g_object_disconnect (obj->conn
|
|
,"any_signal", vn_gui_on_conn_error, obj
|
|
,"any_signal", vn_gui_on_conn_status_changed, obj
|
|
,NULL
|
|
);
|
|
|
|
for (n = obj->windows; n; n = n->next)
|
|
vn_gui_free_window (obj, n->data);
|
|
|
|
g_slist_free (obj->windows);
|
|
obj->windows = NULL;
|
|
obj->active_window = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Idle function that completes the reopen thread.
|
|
*/
|
|
static gboolean vn_gui_reconnect_idle (GuiData * gui_data)
|
|
{
|
|
if (!gui_data->aux)
|
|
vn_gui_show_error (gui_data->obj, gui_data->error);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
/*
|
|
* Thread function that tries to reopen the connection asynchronously.
|
|
*/
|
|
static void vn_gui_reconnect_thread (GuiData * gui_data)
|
|
{
|
|
gui_data->aux = db_conn_reconnect (gui_data->obj->conn, &gui_data->error);
|
|
g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
|
|
(GSourceFunc) vn_gui_reconnect_idle, gui_data, (GDestroyNotify) gui_data_free);
|
|
}
|
|
|
|
/*
|
|
* Reconnects to database.
|
|
*/
|
|
void vn_gui_reconnect (VnGui * obj)
|
|
{
|
|
GuiData * gui_data = g_new (GuiData, 1);
|
|
gui_data->obj = g_object_ref (obj);
|
|
gui_data->aux = FALSE;
|
|
gui_data->error = NULL;
|
|
gui_data->thread = g_thread_new ("vn-gui-reconnect",
|
|
(GThreadFunc) vn_gui_reconnect_thread, gui_data);
|
|
}
|
|
|
|
/*
|
|
* Saves the login information and closes the main GUI.
|
|
*/
|
|
static gboolean vn_gui_logout_idle (GuiData * gui_data)
|
|
{
|
|
vn_gui_close (gui_data->obj);
|
|
|
|
if (gui_data->aux)
|
|
g_signal_emit (gui_data->obj, signals[EXIT], 0);
|
|
else
|
|
g_signal_emit (gui_data->obj, signals[LOGOUT], 0);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
/*
|
|
* Thread function that tryes to close the connection asynchronously.
|
|
*/
|
|
static void vn_gui_logout_thread (GuiData * gui_data)
|
|
{
|
|
db_conn_close (gui_data->obj->conn, TRUE);
|
|
g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
|
|
(GSourceFunc) vn_gui_logout_idle, gui_data, (GDestroyNotify) gui_data_free);
|
|
}
|
|
|
|
/*
|
|
* Closes the connection and the user interface.
|
|
*/
|
|
void vn_gui_logout (VnGui * obj, gboolean exit)
|
|
{
|
|
GuiData * gui_data = g_new (GuiData, 1);
|
|
gui_data->obj = g_object_ref (obj);
|
|
gui_data->aux = exit;
|
|
gui_data->error = NULL;
|
|
gui_data->thread = g_thread_new ("vn-gui-close",
|
|
(GThreadFunc) vn_gui_logout_thread, gui_data);
|
|
}
|
|
|
|
static void vn_gui_hide_form (VnWindow * window)
|
|
{
|
|
VnForm * form = window->active_form;
|
|
|
|
if (form)
|
|
{
|
|
gint i, size;
|
|
const GActionEntry * actions = vn_form_get_actions (form, &size);
|
|
GMenuModel * menu = vn_form_get_menu_model (form);
|
|
|
|
for (i = 0; i < size; i++)
|
|
g_action_map_remove_action (G_ACTION_MAP (window->widget),
|
|
actions[i].name);
|
|
|
|
if (menu)
|
|
vn_gui_set_menu_accels (window->obj, menu, FALSE);
|
|
|
|
gtk_widget_hide (window->menu_button);
|
|
window->active_form = NULL;
|
|
}
|
|
}
|
|
|
|
static void vn_gui_set_show_tabs (VnWindow * window)
|
|
{
|
|
gtk_notebook_set_show_tabs (window->notebook,
|
|
gtk_notebook_get_n_pages (window->notebook) > 1);
|
|
}
|
|
|
|
//--------------------------------------------------- Window handlers
|
|
|
|
/*
|
|
* Called when the last window is closed.
|
|
*/
|
|
gboolean vn_gui_on_last_window_deleted (GtkWidget * widget, GdkEvent * event, VnWindow * window)
|
|
{
|
|
VnGui * obj = window->obj;
|
|
|
|
if (obj->windows->next)
|
|
return FALSE;
|
|
|
|
vn_gui_logout (obj, TRUE);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Called when a window is closed.
|
|
*/
|
|
void vn_gui_on_window_destroyed (GtkWindow * widget, VnWindow * window)
|
|
{
|
|
VnGui * obj = window->obj;
|
|
vn_gui_free_window (obj, window);
|
|
obj->windows = g_slist_remove (obj->windows, window);
|
|
}
|
|
|
|
/*
|
|
* Called when any window of the application gets the focus.
|
|
*/
|
|
gboolean vn_gui_on_window_focused (GtkWidget * widget, GdkEvent * event, VnWindow * window)
|
|
{
|
|
window->obj->active_window = window;
|
|
return FALSE;
|
|
}
|
|
|
|
//--------------------------------------------------- Notebook handlers
|
|
|
|
/*
|
|
* Called when a page is detached from a notebook. This function creates a new
|
|
* window with a notebook and puts the page in.
|
|
* Connected to the "create-window" signal of GtkNotebook.
|
|
*/
|
|
GtkNotebook * vn_gui_on_page_detached (GtkNotebook * old_notebook,
|
|
GtkWidget * page, gint x, gint y, VnWindow * window)
|
|
{
|
|
VnWindow * new_window = vn_gui_create_window (window->obj, x, y);
|
|
return (window) ? new_window->notebook : NULL;
|
|
}
|
|
|
|
/*
|
|
* Called when the focus changes from a page to another. It is also used in
|
|
* newly opened pages.
|
|
*/
|
|
void vn_gui_on_switch_page (GtkNotebook * notebook, VnForm * form, guint num, VnWindow * window)
|
|
{
|
|
GtkTreeIter * iter;
|
|
GMenuModel * menu;
|
|
VnGui * obj = window->obj;
|
|
|
|
vn_gui_hide_form (window);
|
|
window->active_form = form;
|
|
|
|
if ((iter = g_hash_table_lookup (obj->forms, vn_form_get_name (form))))
|
|
{
|
|
gchar * form_title;
|
|
|
|
gtk_tree_model_get (GTK_TREE_MODEL (obj->tree),
|
|
iter, COL_TITLE, &form_title, -1);
|
|
gtk_header_bar_set_title (window->header, form_title);
|
|
g_free (form_title);
|
|
}
|
|
|
|
// Set active form Menu
|
|
|
|
menu = vn_form_get_menu_model (form);
|
|
|
|
if (menu)
|
|
{
|
|
gint size;
|
|
const GActionEntry * actions = vn_form_get_actions (form, &size);
|
|
|
|
g_action_map_add_action_entries (G_ACTION_MAP (window->widget),
|
|
actions, size, form);
|
|
|
|
vn_gui_set_menu_accels (obj, menu, TRUE);
|
|
|
|
gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (window->menu_button), menu);
|
|
gtk_widget_show (window->menu_button);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Called when a page is removed from its notebook. If the window is not the
|
|
* last one and its the last page destroys the window too.
|
|
*/
|
|
void vn_gui_on_page_removed (GtkNotebook * notebook,
|
|
GtkWidget * page, guint num, VnWindow * window)
|
|
{
|
|
if (gtk_notebook_get_n_pages (notebook) < 1)
|
|
{
|
|
if (!window->obj->windows->next)
|
|
{
|
|
vn_gui_hide_form (window);
|
|
gtk_header_bar_set_title (window->header, window->obj->app_title);
|
|
}
|
|
else
|
|
gtk_widget_destroy (GTK_WIDGET (window->widget));
|
|
}
|
|
|
|
vn_gui_set_show_tabs (window);
|
|
}
|
|
|
|
/*
|
|
* Called each time a page is added to a notebook.
|
|
*/
|
|
void vn_gui_on_page_added (GtkNotebook * notebook, GtkWidget * page, guint num,
|
|
VnWindow * window)
|
|
{
|
|
vn_gui_set_show_tabs (window);
|
|
}
|
|
|
|
//--------------------------------------------------- Action handlers
|
|
|
|
/*
|
|
* Opens a form when the action associated to it is activated.
|
|
*/
|
|
void vn_gui_on_open_form_activated (GSimpleAction * a, GVariant * p, gpointer obj)
|
|
{
|
|
VnWindow * window = obj;
|
|
vn_gui_open_form_at_window (window->obj, g_variant_get_string (p, NULL), window);
|
|
}
|
|
|
|
/*
|
|
* New empty window action handler.
|
|
*/
|
|
void vn_gui_on_new_window_activated (GSimpleAction * a, GVariant * v, gpointer obj)
|
|
{
|
|
gint x , y;
|
|
VnWindow * w;
|
|
VnGui * o = obj;
|
|
|
|
gtk_window_get_position (o->active_window->widget, &x, &y);
|
|
w = vn_gui_create_window (obj, x + 100, y + 100);
|
|
gtk_window_resize (w->widget, 500, 500);
|
|
}
|
|
|
|
/*
|
|
* Logout action handler.
|
|
*/
|
|
void vn_gui_on_logout_activated (GSimpleAction * a, GVariant * v, gpointer obj)
|
|
{
|
|
vn_gui_logout (obj, FALSE);
|
|
}
|
|
|
|
/*
|
|
* Reconnects to database.
|
|
*/
|
|
void vn_gui_on_open_activated (GSimpleAction * a, GVariant * v, gpointer obj)
|
|
{
|
|
vn_gui_reconnect (obj);
|
|
}
|
|
|
|
/*
|
|
* Shows a window with program information.
|
|
*/
|
|
void vn_gui_on_about_activated (GSimpleAction * a, GVariant * v, gpointer obj)
|
|
{
|
|
gtk_dialog_run (VN_GUI (obj)->active_window->about);
|
|
}
|
|
|
|
/*
|
|
* Exit action handler.
|
|
*/
|
|
void vn_gui_on_exit_activated (GSimpleAction * a, GVariant * v, gpointer obj)
|
|
{
|
|
vn_gui_logout (obj, TRUE);
|
|
}
|
|
|
|
/*
|
|
* Closes the current tab when the close-tab action is activated.
|
|
*/
|
|
void vn_gui_on_close_tab_activated (GSimpleAction * a, GVariant * v, gpointer w)
|
|
{
|
|
VnWindow * window = w;
|
|
VnGui * obj = window->obj;
|
|
|
|
if (window->active_form)
|
|
vn_gui_close_form (window->obj, window->active_form);
|
|
else if (!obj->windows->next)
|
|
vn_gui_logout (obj, TRUE);
|
|
else
|
|
gtk_widget_destroy (GTK_WIDGET (window->widget));
|
|
}
|
|
|
|
//--------------------------------------------------- Connection handlers
|
|
|
|
/*
|
|
* Called when there is a query error in the connection.
|
|
*/
|
|
static void vn_gui_on_conn_error (DbConn * conn, const GError * error, VnGui * obj)
|
|
{
|
|
vn_gui_show_error (obj, error);
|
|
}
|
|
|
|
/*
|
|
* Enables/disables the #GtkSpinner when connection is loading.
|
|
*/
|
|
static void vn_gui_on_conn_status_changed (DbConn * conn, DbConnStatus status, VnGui * obj)
|
|
{
|
|
GSList * l;
|
|
gchar * status_text;
|
|
|
|
if (status & DB_CONN_CLOSING)
|
|
status_text = _("Closing connection");
|
|
else if (status & DB_CONN_TRANSACTION)
|
|
status_text = _("Transaction started");
|
|
else if (status & DB_CONN_OPENING)
|
|
status_text = _("Connecting");
|
|
else if (status & DB_CONN_LOST)
|
|
status_text = _("Connection lost");
|
|
else if (status == DB_CONN_CLOSED)
|
|
status_text = _("Connection closed");
|
|
else if (status & DB_CONN_LOADING)
|
|
status_text = _("Loading");
|
|
else
|
|
status_text = _("Ready");
|
|
|
|
for (l = obj->windows; l; l = l->next)
|
|
{
|
|
VnWindow * win = l->data;
|
|
gtk_widget_set_tooltip_text (win->spinner, status_text);
|
|
|
|
if (status & DB_CONN_LOADING)
|
|
{
|
|
gtk_widget_set_opacity (win->spinner, 1);
|
|
gtk_spinner_start (GTK_SPINNER (win->spinner));
|
|
}
|
|
else
|
|
{
|
|
gtk_widget_set_opacity (win->spinner, 0);
|
|
gtk_spinner_stop (GTK_SPINNER (win->spinner));
|
|
}
|
|
}
|
|
}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public
|
|
|
|
/**
|
|
* vn_gui_open:
|
|
* @obj: a #VnGui
|
|
*
|
|
* Shows the main GUI.
|
|
**/
|
|
void vn_gui_open (VnGui * obj)
|
|
{
|
|
GtkBuilder * builder;
|
|
GKeyFile * config;
|
|
GError * err = NULL;
|
|
|
|
g_return_if_fail (VN_IS_GUI (obj));
|
|
|
|
builder = gtk_builder_new ();
|
|
|
|
// Loading Application Menu
|
|
|
|
if (gtk_builder_add_from_file (builder, MENU_UI, &err))
|
|
{
|
|
GSList * n;
|
|
GMenu * section;
|
|
GMenuModel * menu = G_MENU_MODEL (gtk_builder_get_object (builder, "app-menu"));
|
|
|
|
g_action_map_add_action_entries (G_ACTION_MAP (obj->app), app_entries,
|
|
G_N_ELEMENTS (app_entries), obj);
|
|
gtk_application_set_app_menu (obj->app, menu);
|
|
|
|
obj->main_menu = gtk_builder_get (builder, "win-menu");
|
|
section = gtk_builder_get (builder, "modules");
|
|
|
|
for (n = obj->modules; n; n = n->next)
|
|
{
|
|
VnMod * mod = VN_MOD (n->data);
|
|
GMenuModel * mod_menu = vn_mod_get_menu_model (mod);
|
|
|
|
if (mod_menu)
|
|
{
|
|
g_menu_prepend_submenu (section,
|
|
g_strdup (vn_mod_get_title (mod)), mod_menu);
|
|
g_object_unref (mod_menu);
|
|
}
|
|
}
|
|
}
|
|
else if (err)
|
|
{
|
|
g_warning ("VnGui: %s", err->message);
|
|
g_error_free (err);
|
|
}
|
|
|
|
// Restoring interface
|
|
|
|
config = g_key_file_new ();
|
|
|
|
if (g_key_file_load_from_file (config, obj->config_file, 0, NULL))
|
|
{
|
|
gsize m, n, len;
|
|
gint x, y, width, height;
|
|
gboolean maximized;
|
|
gchar ** windows, ** forms;
|
|
VnWindow * window;
|
|
|
|
windows = g_key_file_get_groups (config, &len);
|
|
|
|
for (m = 0; m < len; m++)
|
|
{
|
|
x = g_key_file_get_integer (config, windows[m], "x", NULL);
|
|
y = g_key_file_get_integer (config, windows[m], "y", NULL);
|
|
width = g_key_file_get_integer (config, windows[m], "width", NULL);
|
|
height = g_key_file_get_integer (config, windows[m], "height", NULL);
|
|
maximized = g_key_file_get_boolean (config, windows[m], "maximized", NULL);
|
|
|
|
window = vn_gui_create_window (obj, x, y);
|
|
gtk_window_resize (window->widget, width, height);
|
|
|
|
if (maximized)
|
|
gtk_window_maximize (window->widget);
|
|
|
|
forms = g_key_file_get_string_list (config, windows[m], "forms", NULL, NULL);
|
|
|
|
for (n = 0; forms[n]; n++)
|
|
vn_gui_open_form_at_window (obj, forms[n], window);
|
|
|
|
g_key_file_remove_group (config, windows[m], NULL);
|
|
g_strfreev (forms);
|
|
}
|
|
|
|
g_strfreev (windows);
|
|
}
|
|
else
|
|
{
|
|
VnWindow * window = vn_gui_create_window (obj, 100, 100);
|
|
gtk_window_resize (window->widget, 500, 400);
|
|
}
|
|
|
|
g_key_file_free (config);
|
|
|
|
g_object_connect (obj->conn
|
|
,"signal::error", vn_gui_on_conn_error, obj
|
|
,"signal::status-changed", vn_gui_on_conn_status_changed, obj
|
|
,NULL
|
|
);
|
|
|
|
g_object_unref (builder);
|
|
}
|
|
|
|
/**
|
|
* vn_gui_save:
|
|
* @obj: the #VnGui
|
|
*
|
|
* Saves the GUI configuration.
|
|
**/
|
|
void vn_gui_save (VnGui * obj)
|
|
{
|
|
gint len;
|
|
gint j, i = 0;
|
|
gchar * group;
|
|
const gchar ** forms;
|
|
GSList * m;
|
|
GList * nb_pages, * n;
|
|
VnWindow * window;
|
|
gint x, y, width, height;
|
|
GdkWindow * win;
|
|
gboolean maximized;
|
|
GKeyFile * config;
|
|
|
|
g_return_if_fail (VN_IS_GUI (obj));
|
|
|
|
// Saving the interface configuration
|
|
|
|
config = g_key_file_new ();
|
|
|
|
for (m = obj->windows; m; m = m->next)
|
|
{
|
|
window = (VnWindow *) m->data;
|
|
group = g_strdup_printf ("window%d", i++);
|
|
|
|
// Saving the window position and size
|
|
|
|
gtk_window_get_position (window->widget, &x, &y);
|
|
gtk_window_get_size (window->widget, &width, &height);
|
|
|
|
g_key_file_set_integer (config, group, "x", x);
|
|
g_key_file_set_integer (config, group, "y", y);
|
|
g_key_file_set_integer (config, group, "width", width);
|
|
g_key_file_set_integer (config, group, "height", height);
|
|
|
|
g_object_get (window->widget, "window", &win, NULL);
|
|
maximized = (gdk_window_get_state (win) & GDK_WINDOW_STATE_MAXIMIZED);
|
|
g_object_unref (win);
|
|
g_key_file_set_boolean (config, group, "maximized", maximized);
|
|
|
|
// Saving the forms opened at window
|
|
|
|
nb_pages = gtk_container_get_children (GTK_CONTAINER (window->notebook));
|
|
len = gtk_notebook_get_n_pages (window->notebook);
|
|
forms = g_new (const gchar *, len);
|
|
|
|
for (j = 0, n = nb_pages; n; n = n->next)
|
|
forms[j++] = vn_form_get_name (n->data);
|
|
|
|
g_key_file_set_string_list (config, group, "forms", forms, len);
|
|
|
|
g_list_free (nb_pages);
|
|
g_free (forms);
|
|
g_free (group);
|
|
}
|
|
|
|
gvn_key_file_save (config, obj->config_file);
|
|
g_key_file_free (config);
|
|
}
|
|
|
|
/**
|
|
* vn_gui_create_window:
|
|
* @obj: the #VnGui
|
|
* @x: the x coordinate
|
|
* @y: the y coordinate
|
|
*
|
|
* Creates a new window.
|
|
*
|
|
* Return value: VnWindow.
|
|
**/
|
|
VnWindow * vn_gui_create_window (VnGui * obj, gint x, gint y)
|
|
{
|
|
GtkBuilder * builder;
|
|
VnWindow * window = NULL;
|
|
GError * err = NULL;
|
|
|
|
g_return_val_if_fail (VN_IS_GUI (obj), NULL);
|
|
|
|
builder = gtk_builder_new ();
|
|
|
|
if (gtk_builder_add_from_file (builder, MAIN_UI, &err))
|
|
{
|
|
GtkWindow * widget = gtk_builder_get (builder, "window");
|
|
GtkNotebook * notebook = gtk_builder_get (builder, "notebook");
|
|
gtk_window_move (widget, x, y);
|
|
|
|
window = vn_gui_add_window (obj, widget, notebook);
|
|
window->about = gtk_builder_get (builder, "about");
|
|
gtk_builder_connect_signals (builder, window);
|
|
}
|
|
else
|
|
{
|
|
g_warning ("VnGui: %s", err->message);
|
|
g_error_free (err);
|
|
}
|
|
|
|
g_object_unref (builder);
|
|
return window;
|
|
}
|
|
|
|
/**
|
|
* vn_gui_open_form_at_window:
|
|
* @obj: the #VnGui
|
|
* @form_name: the name of the form that you want to open
|
|
* @window: the #VnWindow where the form will be opened
|
|
*
|
|
* Opens a new form at the specified window.
|
|
*
|
|
* Return value: (transfer none): the created #VnForm
|
|
**/
|
|
VnForm * vn_gui_open_form_at_window (VnGui * obj, const gchar * form_name, VnWindow * window)
|
|
{
|
|
gchar * icon;
|
|
gchar * title;
|
|
VnMod * module;
|
|
GType form_type;
|
|
GtkBox * hbox;
|
|
GtkWidget * form;
|
|
GtkWidget * widget;
|
|
GtkWidget * button;
|
|
GtkTreeIter * iter;
|
|
GtkNotebook * notebook = NULL;
|
|
|
|
g_return_val_if_fail (VN_IS_GUI (obj), NULL);
|
|
|
|
iter = g_hash_table_lookup (obj->forms, form_name);
|
|
|
|
if (!iter)
|
|
{
|
|
g_warning ("VnGui: Form %s doesn't exist", form_name);
|
|
return NULL;
|
|
}
|
|
|
|
gtk_tree_model_get (GTK_TREE_MODEL (obj->tree), iter
|
|
,COL_ICON ,&icon
|
|
,COL_TITLE ,&title
|
|
,COL_MODULE ,&module
|
|
,COL_TYPE ,&form_type
|
|
,-1
|
|
);
|
|
|
|
form = g_object_new (form_type
|
|
,"name" ,form_name
|
|
,"gui" ,obj
|
|
,"module" ,module
|
|
,NULL
|
|
);
|
|
vn_form_open (VN_FORM (form));
|
|
|
|
hbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5));
|
|
|
|
widget = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU);
|
|
gtk_box_pack_start (hbox, widget, FALSE, FALSE, 0);
|
|
|
|
widget = gtk_label_new (title);
|
|
gtk_box_pack_start (hbox, widget, TRUE, TRUE, 0);
|
|
|
|
button = gtk_button_new ();
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (vn_gui_close_form), form);
|
|
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
|
|
gtk_box_pack_start (hbox, button, FALSE, FALSE, 0);
|
|
|
|
widget = gtk_image_new_from_icon_name ("window-close-symbolic", GTK_ICON_SIZE_MENU);
|
|
gtk_button_set_image (GTK_BUTTON (button), widget);
|
|
|
|
if (!window)
|
|
window = obj->active_window;
|
|
|
|
notebook = window->notebook;
|
|
gtk_notebook_set_current_page (notebook,
|
|
gtk_notebook_append_page (notebook, form, GTK_WIDGET (hbox)));
|
|
gtk_notebook_set_tab_detachable (notebook, form, TRUE);
|
|
gtk_notebook_set_tab_reorderable (notebook, form, TRUE);
|
|
gtk_container_child_set (GTK_CONTAINER (notebook), form,
|
|
"tab-expand", TRUE, NULL);
|
|
gtk_widget_show_all (GTK_WIDGET (hbox));
|
|
gtk_widget_show (form);
|
|
|
|
g_free (icon);
|
|
g_free (title);
|
|
|
|
return VN_FORM (form);
|
|
}
|
|
|
|
/**
|
|
* vn_gui_open_form:
|
|
* @obj: the #VnGui
|
|
* @form_name: the name of the form that you want to open
|
|
*
|
|
* Opens a new form, creating a new page for it at the main notebook.
|
|
*
|
|
* Return value: (transfer none): the created #VnForm
|
|
**/
|
|
VnForm * vn_gui_open_form (VnGui * obj, const gchar * form_name)
|
|
{
|
|
g_return_val_if_fail (VN_IS_GUI (obj), NULL);
|
|
g_return_val_if_fail (form_name, NULL);
|
|
|
|
return vn_gui_open_form_at_window (obj, form_name, NULL);
|
|
}
|
|
|
|
/**
|
|
* vn_gui_close_form:
|
|
* @obj: the #VnGui
|
|
* @form: the #VnForm that you want to close
|
|
*
|
|
* Closes a form.
|
|
**/
|
|
void vn_gui_close_form (VnGui * obj, VnForm * form)
|
|
{
|
|
gint num;
|
|
GtkNotebook * notebook;
|
|
|
|
g_return_if_fail (VN_IS_FORM (form));
|
|
|
|
notebook = GTK_NOTEBOOK (gtk_widget_get_ancestor (
|
|
GTK_WIDGET (form), GTK_TYPE_NOTEBOOK));
|
|
num = gtk_notebook_page_num (notebook, GTK_WIDGET (form));
|
|
gtk_notebook_remove_page (notebook, num);
|
|
}
|
|
|
|
/**
|
|
* vn_gui_get_conn:
|
|
* @obj: a #VnGui
|
|
*
|
|
* Gets the Data Base connection used by @obj
|
|
*
|
|
* Return value: (transfer none): the #DbConn
|
|
**/
|
|
DbConn * vn_gui_get_conn (VnGui * obj)
|
|
{
|
|
g_return_val_if_fail (VN_IS_GUI (obj), NULL);
|
|
|
|
return obj->conn;
|
|
}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties
|
|
|
|
enum
|
|
{
|
|
PROP_CONN = 1
|
|
,PROP_APP
|
|
};
|
|
|
|
static void vn_gui_set_property (VnGui * obj, guint id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
switch (id)
|
|
{
|
|
case PROP_CONN:
|
|
{
|
|
gchar * query_path = g_build_path (G_SEARCHPATH_SEPARATOR_S
|
|
,_VN_MODULE_QUERY_DIR
|
|
,g_getenv ("VN_MODULE_QUERY_PATH")
|
|
,NULL
|
|
);
|
|
|
|
obj->conn = g_value_dup_object (value);
|
|
db_conn_set_query_path (obj->conn, query_path);
|
|
|
|
g_free (query_path);
|
|
break;
|
|
}
|
|
case PROP_APP:
|
|
{
|
|
gint n;
|
|
const gchar * app_id;
|
|
gchar * app_name;
|
|
gchar * config_dir;
|
|
gchar * lib_path, * data_path;
|
|
|
|
obj->app = g_value_dup_object (value);
|
|
|
|
app_id = g_application_get_application_id (G_APPLICATION (obj->app));
|
|
app_name = g_strrstr (app_id, ".") + 1;
|
|
|
|
obj->app_title = g_strdup (app_name);
|
|
obj->app_title[0] = g_ascii_toupper (obj->app_title[0]);
|
|
|
|
config_dir = g_build_filename (g_get_user_config_dir (), app_name, NULL);
|
|
g_mkdir_with_parents (config_dir, 0700);
|
|
obj->config_file = g_build_filename (config_dir, "gui.ini", NULL);
|
|
g_free (config_dir);
|
|
|
|
// Setting module search paths
|
|
lib_path = g_build_path (G_SEARCHPATH_SEPARATOR_S
|
|
,_VN_MODULE_LIB_DIR
|
|
,g_getenv ("VN_MODULE_LIB_PATH")
|
|
,NULL
|
|
);
|
|
data_path = g_build_path (G_SEARCHPATH_SEPARATOR_S
|
|
,_VN_MODULE_DATA_DIR
|
|
,g_getenv ("VN_MODULE_DATA_PATH")
|
|
,NULL
|
|
);
|
|
|
|
obj->lib_dirs = g_strsplit (lib_path, G_SEARCHPATH_SEPARATOR_S, 0);
|
|
obj->data_dirs = g_strsplit (data_path, G_SEARCHPATH_SEPARATOR_S, 0);
|
|
|
|
g_free (data_path);
|
|
g_free (lib_path);
|
|
|
|
// Initializing modules
|
|
|
|
for (n = 0; obj->data_dirs[n]; n++)
|
|
{
|
|
const gchar * file;
|
|
GError * err = NULL;
|
|
GDir * dir = g_dir_open (obj->data_dirs[n], 0, &err);
|
|
|
|
if (dir)
|
|
{
|
|
while ((file = g_dir_read_name (dir)))
|
|
if (!g_strcmp0 (".xml", g_strrstr (file, ".")))
|
|
vn_gui_load_module (obj, obj->data_dirs[n], file);
|
|
|
|
g_dir_close (dir);
|
|
}
|
|
else
|
|
{
|
|
g_warning ("VnGui: Error opening directory at module data path: %s"
|
|
,err->message);
|
|
g_error_free (err);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec);
|
|
}
|
|
}
|
|
|
|
static void vn_gui_get_property (VnGui * obj, guint id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
switch (id)
|
|
{
|
|
case PROP_CONN:
|
|
g_value_set_object (value, obj->conn);
|
|
break;
|
|
case PROP_APP:
|
|
g_value_set_object (value, obj->app);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec);
|
|
}
|
|
}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class
|
|
|
|
static void vn_gui_init (VnGui * obj)
|
|
{
|
|
obj->active_window = NULL;
|
|
obj->windows = NULL;
|
|
obj->conn = NULL;
|
|
obj->app = NULL;
|
|
obj->app_title = NULL;
|
|
obj->modules = NULL;
|
|
obj->lib_dirs = NULL;
|
|
obj->data_dirs = NULL;
|
|
obj->config_file = NULL;
|
|
obj->main_menu = NULL;
|
|
obj->modules = NULL;
|
|
|
|
obj->forms = g_hash_table_new_full (
|
|
(GHashFunc) g_str_hash
|
|
,(GEqualFunc) g_str_equal
|
|
,(GDestroyNotify) g_free
|
|
,(GDestroyNotify) g_free
|
|
);
|
|
obj->tree = gtk_tree_store_new (COL_COUNT
|
|
,G_TYPE_STRING // COL_ICON
|
|
,G_TYPE_STRING // COL_NAME
|
|
,G_TYPE_STRING // COL_TITLE
|
|
,G_TYPE_GTYPE // COL_TYPE
|
|
,G_TYPE_OBJECT // COL_MODULE
|
|
);
|
|
}
|
|
|
|
static void vn_gui_finalize (VnGui * obj)
|
|
{
|
|
vn_gui_close (obj);
|
|
db_conn_close (obj->conn, FALSE);
|
|
|
|
g_hash_table_unref (obj->forms);
|
|
g_slist_free_full (obj->modules, g_object_unref);
|
|
g_clear_object (&obj->conn);
|
|
g_clear_object (&obj->tree);
|
|
g_clear_object (&obj->app);
|
|
g_strfreev (obj->lib_dirs);
|
|
g_strfreev (obj->data_dirs);
|
|
g_free (obj->config_file);
|
|
g_free (obj->app_title);
|
|
G_OBJECT_CLASS (vn_gui_parent_class)->finalize (G_OBJECT (obj));
|
|
}
|
|
|
|
static void vn_gui_class_init (VnGuiClass * k)
|
|
{
|
|
GObjectClass * klass = G_OBJECT_CLASS (k);
|
|
klass->finalize = (GObjectFinalizeFunc) vn_gui_finalize;
|
|
klass->set_property = (GObjectSetPropertyFunc) vn_gui_set_property;
|
|
klass->get_property = (GObjectGetPropertyFunc) vn_gui_get_property;
|
|
|
|
signals[LOGOUT] = g_signal_new ("logout",
|
|
VN_TYPE_GUI, G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0
|
|
);
|
|
signals[EXIT] = g_signal_new ("exit",
|
|
VN_TYPE_GUI, G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0
|
|
);
|
|
|
|
g_object_class_install_property (klass, PROP_CONN,
|
|
g_param_spec_object ("conn"
|
|
,_("Connection")
|
|
,_("The connection used by Gui")
|
|
,DB_TYPE_CONN
|
|
,G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
|
|
));
|
|
g_object_class_install_property (klass, PROP_APP,
|
|
g_param_spec_object ("app"
|
|
,_("Application")
|
|
,_("The application handler for the entire program")
|
|
,GTK_TYPE_APPLICATION
|
|
,G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
|
|
));
|
|
}
|