medit/moo/mooedit/moousertools.c

776 lines
21 KiB
C

/*
* moousertools.c
*
* Copyright (C) 2004-2006 by Yevgen Muntyan <muntyan@math.tamu.edu>
*
* 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 2 of the License, or
* (at your option) any later version.
*
* See COPYING file that comes with this distribution.
*/
#include "mooedit/moousertools.h"
#include "mooedit/mooeditwindow.h"
#include "mooedit/mooedit-script.h"
#include "mooedit/moocmdview.h"
#include "mooedit/mooedit-actions.h"
#include "mooutils/mooutils-misc.h"
#include "mooutils/mooconfig.h"
#include "mooutils/moocommand.h"
#include "mooutils/mooaccel.h"
#include "mooutils/mooaction.h"
#include <string.h>
typedef enum {
FILE_TOOLS,
FILE_MENU
} FileType;
typedef enum {
ACTION_NEED_DOC = 1 << 0,
ACTION_NEED_FILE = 1 << 1,
ACTION_NEED_SAVE = 1 << 2,
ACTION_SILENT = 1 << 3
} ActionOptions;
typedef struct {
char *id;
char *name;
char *label;
char *accel;
GSList *langs;
MooCommand *cmd;
MooUIXML *xml;
guint merge_id;
FileType type;
ActionOptions options;
} ActionData;
static GSList *tools_actions;
static GSList *menu_actions;
static void remove_tools (void);
static void remove_menu_actions (void);
static void load_config_item (FileType type,
MooConfig *config,
MooConfigItem *item,
MooUIXML *xml);
static GtkAction *create_tool_action (MooWindow *window,
gpointer user_data);
static GtkAction *create_edit_action (MooEdit *edit,
gpointer user_data);
static void check_visible_func (GtkAction *action,
MooEdit *doc,
GParamSpec *pspec,
GValue *prop_value,
gpointer dummy);
static void check_sensitive_func(GtkAction *action,
MooEdit *doc,
GParamSpec *pspec,
GValue *prop_value,
gpointer dummy);
static ActionData *action_data_new (FileType type,
const char *name,
const char *label,
const char *accel,
GSList *langs,
MooCommand *cmd,
ActionOptions options);
static void action_data_free (ActionData *data);
static void
get_files (FileType type,
char ***files_p,
guint *n_files_p,
char **user_file_p)
{
guint n_files = 0, i;
char **files = NULL;
char *user_file = NULL;
GSList *list = NULL;
switch (type)
{
case FILE_TOOLS:
user_file = moo_get_user_data_file (MOO_USER_TOOLS_FILE);
files = moo_get_data_files (MOO_USER_TOOLS_FILE,
MOO_DATA_SHARE, &n_files);
break;
case FILE_MENU:
user_file = moo_get_user_data_file (MOO_USER_MENU_FILE);
files = moo_get_data_files (MOO_USER_MENU_FILE,
MOO_DATA_SHARE, &n_files);
break;
}
if (n_files)
{
int i;
for (i = n_files - 1; i >= 0; --i)
if (!user_file || strcmp (user_file, files[i]))
if (g_file_test (files[i], G_FILE_TEST_EXISTS))
list = g_slist_prepend (list, g_strdup (files[i]));
}
g_strfreev (files);
list = g_slist_reverse (list);
n_files = g_slist_length (list);
files = g_new (char*, n_files + 1);
files[n_files] = NULL;
for (i = 0; i < n_files; ++i)
{
files[i] = list->data;
list = g_slist_delete_link (list, list);
}
*user_file_p = user_file;
*files_p = files;
*n_files_p = n_files;
}
void
moo_edit_get_user_tools_files (char ***default_files,
guint *n_files,
char **user_file)
{
return get_files (FILE_TOOLS, default_files, n_files, user_file);
}
void
moo_edit_get_user_menu_files (char ***default_files,
guint *n_files,
char **user_file)
{
return get_files (FILE_MENU, default_files, n_files, user_file);
}
static void
moo_edit_load_tools (FileType type,
char **default_files,
guint n_files,
char *user_file,
MooUIXML *xml)
{
guint i, n_items;
MooConfig *config;
switch (type)
{
case FILE_TOOLS:
remove_tools ();
break;
case FILE_MENU:
remove_menu_actions ();
break;
}
if (!n_files && !user_file)
return;
config = moo_config_new ();
moo_config_set_default_bool (config, MOO_USER_TOOL_KEY_ENABLED, TRUE);
for (i = 0; i < n_files; ++i)
moo_config_parse_file (config, default_files[i], FALSE, NULL);
if (user_file && g_file_test (user_file, G_FILE_TEST_EXISTS))
moo_config_parse_file (config, user_file, FALSE, NULL);
n_items = moo_config_n_items (config);
for (i = 0; i < n_items; ++i)
load_config_item (type, config, moo_config_nth_item (config, i), xml);
g_object_unref (config);
}
void
moo_edit_load_user_tools (char **default_files,
guint n_files,
char *user_file,
MooUIXML *xml)
{
moo_edit_load_tools (FILE_TOOLS, default_files, n_files, user_file, xml);
}
void
moo_edit_load_user_menu (char **default_files,
guint n_files,
char *user_file,
MooUIXML *xml)
{
moo_edit_load_tools (FILE_MENU, default_files, n_files, user_file, xml);
}
static GSList *
config_item_get_langs (MooConfigItem *item)
{
const char *string;
char **pieces, **p;
GSList *list = NULL;
string = moo_config_item_get (item, MOO_USER_TOOL_KEY_LANG);
if (!string)
return NULL;
pieces = g_strsplit_set (string, " \t\r\n;,", 0);
if (!pieces)
return NULL;
for (p = pieces; p && *p; ++p)
if (**p)
list = g_slist_prepend (list, moo_lang_id_from_name (*p));
g_strfreev (pieces);
return g_slist_reverse (list);
}
static MooCommand *
config_item_get_command (MooConfigItem *item)
{
const char *code, *type;
MooCommandType cmd_type = MOO_COMMAND_SCRIPT;
MooCommand *cmd;
code = moo_config_item_get_content (item);
g_return_val_if_fail (code != NULL, NULL);
type = moo_config_item_get (item, MOO_USER_TOOL_KEY_COMMAND);
if (type)
cmd_type = moo_command_type_parse (type);
g_return_val_if_fail (cmd_type != 0, NULL);
cmd = moo_command_new (cmd_type, code);
return cmd;
}
static ActionOptions
config_item_get_options (MooConfigItem *item)
{
const char *string;
char **pieces, **p;
ActionOptions opts = 0;
string = moo_config_item_get (item, MOO_USER_TOOL_KEY_OPTIONS);
if (!string)
return 0;
pieces = g_strsplit_set (string, " \t\r\n;,", 0);
if (!pieces)
return 0;
for (p = pieces; p && *p; ++p)
{
if (**p)
{
char *opt = g_ascii_strdown (g_strdelimit (*p, "_", '-'), -1);
if (!strcmp (opt, MOO_USER_TOOL_OPTION_NEED_SAVE))
opts |= ACTION_NEED_SAVE;
else if (!strcmp (opt, MOO_USER_TOOL_OPTION_NEED_FILE))
opts |= ACTION_NEED_FILE;
else if (!strcmp (opt, MOO_USER_TOOL_OPTION_NEED_DOC))
opts |= ACTION_NEED_DOC;
else if (!strcmp (opt, MOO_USER_TOOL_OPTION_SILENT))
opts |= ACTION_SILENT;
else
g_warning ("%s: unknown option '%s'", G_STRLOC, opt);
g_free (opt);
}
}
g_strfreev (pieces);
return opts;
}
static void
load_config_item (FileType type,
MooConfig *config,
MooConfigItem *item,
MooUIXML *xml)
{
MooCommand *cmd;
ActionData *data;
ActionOptions options;
GSList *langs;
const char *name, *label, *accel, *pos, *os, *menu;
gboolean enabled;
gpointer klass;
g_return_if_fail (item != NULL);
enabled = moo_config_get_bool (config, item, MOO_USER_TOOL_KEY_ENABLED);
os = moo_config_item_get (item, MOO_USER_TOOL_KEY_OS);
if (!enabled)
return;
if (os)
{
char *norm = g_ascii_strdown (os, -1);
#ifdef __WIN32__
if (!strcmp (norm, "unix"))
{
g_free (norm);
return;
}
#else
if (!strncmp (norm, "win", 3))
{
g_free (norm);
return;
}
#endif
g_free (norm);
}
name = moo_config_item_get (item, MOO_USER_TOOL_KEY_ACTION);
label = moo_config_item_get (item, MOO_USER_TOOL_KEY_LABEL);
accel = moo_config_item_get (item, MOO_USER_TOOL_KEY_ACCEL);
pos = moo_config_item_get (item, MOO_USER_TOOL_KEY_POSITION);
menu = moo_config_item_get (item, MOO_USER_TOOL_KEY_MENU);
g_return_if_fail (name != NULL);
cmd = config_item_get_command (item);
g_return_if_fail (cmd != NULL);
options = config_item_get_options (item);
langs = config_item_get_langs (item);
data = action_data_new (type, name, label, accel, langs, cmd, options);
g_return_if_fail (data != NULL);
switch (type)
{
case FILE_TOOLS:
klass = g_type_class_peek (MOO_TYPE_EDIT_WINDOW);
moo_window_class_new_action_custom (klass, data->id,
create_tool_action, data,
(GDestroyNotify) action_data_free);
if (data->langs)
moo_edit_window_add_action_check (data->id, "visible",
check_visible_func,
NULL, NULL);
if (data->options)
moo_edit_window_add_action_check (data->id, "sensitive",
check_sensitive_func,
NULL, NULL);
break;
case FILE_MENU:
klass = g_type_class_peek (MOO_TYPE_EDIT);
moo_edit_class_new_action_custom (klass, data->id,
create_edit_action, data,
(GDestroyNotify) action_data_free);
break;
}
if (xml)
{
const char *ui_path;
char *freeme = NULL;
char *markup;
markup = g_markup_printf_escaped ("<item action=\"%s\"/>",
data->id);
data->xml = g_object_ref (xml);
data->merge_id = moo_ui_xml_new_merge_id (xml);
if (type == FILE_MENU)
{
ui_path = "Editor/Popup/PopupEnd";
if (pos)
{
char *c = g_ascii_strdown (pos, -1);
if (!strcmp (c, MOO_USER_TOOL_POSITION_END))
ui_path = "Editor/Popup/PopupEnd";
else if (!strcmp (c, MOO_USER_TOOL_POSITION_START))
ui_path = "Editor/Popup/PopupStart";
else
g_warning ("%s: unknown position type '%s'",
G_STRLOC, c);
g_free (c);
}
}
else
{
freeme = g_strdup_printf ("Editor/Menubar/%s/UserMenu",
menu ? menu : "Tools");
ui_path = freeme;
}
moo_ui_xml_insert_markup (xml, data->merge_id, ui_path, -1, markup);
g_free (markup);
g_free (freeme);
}
}
static ActionData *
action_data_new (FileType type,
const char *name,
const char *label,
const char *accel,
GSList *langs,
MooCommand *cmd,
ActionOptions options)
{
ActionData *data;
g_return_val_if_fail (name && name[0], NULL);
g_return_val_if_fail (MOO_IS_COMMAND (cmd), NULL);
data = g_new0 (ActionData, 1);
data->type = type;
data->id = g_strdup (name);
data->name = g_strdup (name);
data->label = label ? g_strdup (label) : g_strdup (name);
data->accel = _moo_accel_normalize (accel);
data->langs = langs;
data->cmd = cmd;
data->options = options;
if (options & ACTION_SILENT)
moo_command_add_flags (cmd, MOO_COMMAND_SILENT);
switch (type)
{
case FILE_TOOLS:
tools_actions = g_slist_prepend (tools_actions, data);
break;
case FILE_MENU:
menu_actions = g_slist_prepend (menu_actions, data);
break;
}
return data;
}
static void
action_data_free (ActionData *data)
{
if (data)
{
gpointer *klass;
if (data->xml)
{
moo_ui_xml_remove_ui (data->xml, data->merge_id);
g_object_unref (data->xml);
}
switch (data->type)
{
case FILE_TOOLS:
tools_actions = g_slist_remove (tools_actions, data);
klass = g_type_class_ref (MOO_TYPE_EDIT_WINDOW);
if (data->langs)
moo_edit_window_remove_action_check (data->id, "visible");
if (data->options)
moo_edit_window_remove_action_check (data->id, "sensitive");
g_type_class_unref (klass);
break;
case FILE_MENU:
menu_actions = g_slist_remove (menu_actions, data);
break;
}
g_free (data->id);
g_free (data->name);
g_free (data->label);
g_free (data->accel);
g_slist_foreach (data->langs, (GFunc) g_free, NULL);
g_slist_free (data->langs);
g_object_unref (data->cmd);
g_free (data);
}
}
static void
add_id (ActionData *data,
GSList **list)
{
g_return_if_fail (data != NULL);
*list = g_slist_prepend (*list, g_strdup (data->id));
}
static void
remove_tools (void)
{
GSList *names = NULL;
MooWindowClass *klass = g_type_class_ref (MOO_TYPE_EDIT_WINDOW);
g_slist_foreach (tools_actions, (GFunc) add_id, &names);
while (names)
{
moo_window_class_remove_action (klass, names->data);
g_free (names->data);
names = g_slist_delete_link (names, names);
}
g_type_class_unref (klass);
}
static void
remove_menu_actions (void)
{
GSList *names = NULL;
MooEditClass *klass = g_type_class_ref (MOO_TYPE_EDIT);
g_slist_foreach (menu_actions, (GFunc) add_id, &names);
while (names)
{
moo_edit_class_remove_action (klass, names->data);
g_free (names->data);
names = g_slist_delete_link (names, names);
}
g_type_class_unref (klass);
}
/****************************************************************************/
/* MooUserToolAction
*/
typedef struct {
MooEditAction parent;
MooEditWindow *window;
ActionData *data;
} MooToolAction;
typedef MooEditActionClass MooToolActionClass;
GType _moo_tool_action_get_type (void) G_GNUC_CONST;
G_DEFINE_TYPE (MooToolAction, _moo_tool_action, MOO_TYPE_EDIT_ACTION);
#define MOO_IS_TOOL_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, _moo_tool_action_get_type()))
#define MOO_TOOL_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, _moo_tool_action_get_type(), MooToolAction))
static gboolean
run_exe (MooToolAction *action,
const char *cmd_line)
{
GtkWidget *cmd_view;
g_return_val_if_fail (MOO_IS_TOOL_ACTION (action), FALSE);
g_return_val_if_fail (MOO_IS_EDIT_WINDOW (action->window), FALSE);
g_return_val_if_fail (cmd_line != NULL, FALSE);
cmd_view = moo_edit_window_get_output (action->window);
g_return_val_if_fail (MOO_IS_CMD_VIEW (cmd_view), FALSE);
moo_line_view_clear (MOO_LINE_VIEW (cmd_view));
moo_big_paned_present_pane (action->window->paned,
moo_edit_window_get_output_pane (action->window));
return moo_cmd_view_run_command (MOO_CMD_VIEW (cmd_view), cmd_line,
action->data->cmd->working_dir,
moo_action_get_display_name (GTK_ACTION (action)));
}
static void
moo_tool_action_activate (GtkAction *_action)
{
MooToolAction *action;
MooEdit *doc;
gboolean silent;
g_return_if_fail (MOO_IS_TOOL_ACTION (_action));
action = MOO_TOOL_ACTION (_action);
g_return_if_fail (action->data != NULL);
doc = MOO_EDIT_ACTION(action)->doc;
doc = doc ? doc : moo_edit_window_get_active_doc (action->window);
if ((action->data->options & ACTION_NEED_DOC) && !doc)
return;
if (action->data->options & ACTION_NEED_SAVE)
if (!doc || !moo_edit_save (doc, NULL))
return;
if (action->data->options & ACTION_NEED_FILE)
if (!doc || !moo_edit_get_filename (doc))
return;
moo_edit_setup_command (action->data->cmd, doc, action->window);
silent = action->data->cmd->flags & MOO_COMMAND_SILENT;
if (action->window && !silent)
g_signal_connect_swapped (action->data->cmd, "run-exe",
G_CALLBACK (run_exe), action);
if (doc && moo_edit_get_filename (doc))
{
char *dir = g_path_get_dirname (moo_edit_get_filename (doc));
moo_command_set_working_dir (action->data->cmd, dir);
g_free (dir);
}
moo_command_run (action->data->cmd);
if (action->window && !silent)
g_signal_handlers_disconnect_by_func (action->data->cmd,
(gpointer) run_exe,
action);
}
static void
_moo_tool_action_class_init (MooToolActionClass *klass)
{
GTK_ACTION_CLASS(klass)->activate = moo_tool_action_activate;
}
static void
_moo_tool_action_init (G_GNUC_UNUSED MooToolAction *action)
{
}
static GtkAction *
create_tool_action (MooWindow *window,
gpointer user_data)
{
ActionData *data = user_data;
MooToolAction *action;
g_return_val_if_fail (MOO_IS_EDIT_WINDOW (window), NULL);
g_return_val_if_fail (data != NULL, NULL);
action = g_object_new (_moo_tool_action_get_type(),
"name", data->name,
"label", data->label,
NULL);
moo_action_set_default_accel (GTK_ACTION (action), data->accel);
action->window = MOO_EDIT_WINDOW (window);
action->data = data;
return GTK_ACTION (action);
}
static GtkAction *
create_edit_action (MooEdit *edit,
gpointer user_data)
{
ActionData *data = user_data;
MooToolAction *action;
MooEditActionFlags flags = 0;
g_return_val_if_fail (MOO_IS_EDIT (edit), NULL);
g_return_val_if_fail (data != NULL, NULL);
if (data->options & ACTION_NEED_FILE)
flags |= MOO_EDIT_ACTION_NEED_FILE;
action = g_object_new (_moo_tool_action_get_type(),
"name", data->name,
"label", data->label,
"doc", edit,
"langs", data->langs,
"flags", flags,
NULL);
moo_action_set_default_accel (GTK_ACTION (action), data->accel);
action->window = moo_edit_get_window (edit);
action->data = data;
return GTK_ACTION (action);
}
static void
check_visible_func (GtkAction *_action,
MooEdit *doc,
G_GNUC_UNUSED GParamSpec *pspec,
GValue *prop_value,
G_GNUC_UNUSED gpointer dummy)
{
MooToolAction *action;
MooLang *lang;
gboolean visible = FALSE;
g_return_if_fail (MOO_IS_TOOL_ACTION (_action));
action = MOO_TOOL_ACTION (_action);
g_return_if_fail (action->data != NULL);
if (doc)
{
lang = moo_text_view_get_lang (MOO_TEXT_VIEW (doc));
visible = NULL != g_slist_find_custom (action->data->langs,
moo_lang_id (lang),
(GCompareFunc) strcmp);
}
g_value_set_boolean (prop_value, visible);
}
static void
check_sensitive_func (GtkAction *_action,
G_GNUC_UNUSED MooEdit *doc,
G_GNUC_UNUSED GParamSpec *pspec,
GValue *prop_value,
G_GNUC_UNUSED gpointer dummy)
{
MooToolAction *action;
gboolean sensitive = TRUE;
g_return_if_fail (MOO_IS_TOOL_ACTION (_action));
action = MOO_TOOL_ACTION (_action);
g_return_if_fail (action->data != NULL);
g_value_set_boolean (prop_value, sensitive);
}