* Copyright 2011 kubtek <kubtek@mail.com>
*
* This file is part of StarDict.
*
* StarDict 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.
*
* StarDict 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 StarDict. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "pluginmanagedlg.h"
#include "desktop.h"
#include <glib/gi18n.h>
#include "stardict.h"
PluginManageDlg::PluginManageDlg()
:
window(NULL),
treeview(NULL),
detail_label(NULL),
pref_button(NULL),
plugin_tree_model(NULL),
dict_changed_(false),
order_changed_(false)
{
}
PluginManageDlg::~PluginManageDlg()
{
g_assert(!window);
g_assert(!treeview);
g_assert(!detail_label);
g_assert(!pref_button);
g_assert(!plugin_tree_model);
}
void PluginManageDlg::response_handler (GtkDialog *dialog, gint res_id, PluginManageDlg *oPluginManageDlg)
{
if (res_id == GTK_RESPONSE_HELP) {
show_help("stardict-plugins");
} else if (res_id == STARDICT_RESPONSE_CONFIGURE) {
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(oPluginManageDlg->treeview));
GtkTreeModel *model;
GtkTreeIter iter;
if (! gtk_tree_selection_get_selected (selection, &model, &iter))
return;
if (!gtk_tree_model_iter_has_child(model, &iter)) {
gchar *filename;
StarDictPlugInType plugin_type;
gtk_tree_model_get (model, &iter, 4, &filename, 5, &plugin_type, -1);
gpAppFrame->oStarDictPlugins->configure_plugin(filename, plugin_type);
g_free(filename);
}
}
}
static gboolean get_disable_list(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
{
if (!gtk_tree_model_iter_has_child(model, iter)) {
gboolean enable;
gtk_tree_model_get (model, iter, 1, &enable, -1);
if (!enable) {
gchar *filename;
gtk_tree_model_get (model, iter, 4, &filename, -1);
std::list<std::string> *disable_list = (std::list<std::string> *)data;
disable_list->push_back(filename);
g_free(filename);
}
}
return FALSE;
}
void PluginManageDlg::on_plugin_enable_toggled (GtkCellRendererToggle *cell, gchar *path_str, PluginManageDlg *oPluginManageDlg)
{
GtkTreeModel *model = GTK_TREE_MODEL(oPluginManageDlg->plugin_tree_model);
GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
GtkTreeIter iter;
gtk_tree_model_get_iter (model, &iter, path);
gtk_tree_path_free (path);
gboolean enable;
gchar *filename;
StarDictPlugInType plugin_type;
gboolean can_configure;
gtk_tree_model_get (model, &iter, 1, &enable, 4, &filename, 5, &plugin_type, 6, &can_configure, -1);
enable = !enable;
gtk_tree_store_set (GTK_TREE_STORE (model), &iter, 1, enable, -1);
if (enable) {
gpAppFrame->oStarDictPlugins->load_plugin(filename);
} else {
gpAppFrame->oStarDictPlugins->unload_plugin(filename, plugin_type);
}
g_free(filename);
if (enable)
gtk_widget_set_sensitive(oPluginManageDlg->pref_button, can_configure);
else
gtk_widget_set_sensitive(oPluginManageDlg->pref_button, FALSE);
if (plugin_type == StarDictPlugInType_VIRTUALDICT
|| plugin_type == StarDictPlugInType_VIRTUALDICT2
|| plugin_type == StarDictPlugInType_NETDICT) {
oPluginManageDlg->dict_changed_ = true;
} else if (plugin_type == StarDictPlugInType_TTS) {
gpAppFrame->oMidWin.oToolWin.UpdatePronounceMenu();
}
std::list<std::string> disable_list;
gtk_tree_model_foreach(model, get_disable_list, &disable_list);
#ifdef _WIN32
{
std::list<std::string> disable_list_rel;
rel_path_to_data_dir(disable_list, disable_list_rel);
std::swap(disable_list, disable_list_rel);
}
#endif
conf->set_strlist("/apps/stardict/manage_plugins/plugin_disable_list", disable_list);
}
struct plugininfo_ParseUserData {
gchar *info_str;
gchar *detail_str;
std::string filename;
std::string name;
std::string version;
std::string short_desc;
std::string long_desc;
std::string author;
std::string website;
};
static void plugininfo_parse_start_element(GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer user_data, GError **error)
{
if (strcmp(element_name, "plugin_info")==0) {
plugininfo_ParseUserData *Data = (plugininfo_ParseUserData *)user_data;
Data->name.clear();
Data->version.clear();
Data->short_desc.clear();
Data->long_desc.clear();
Data->author.clear();
Data->website.clear();
}
}
static void plugininfo_parse_end_element(GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error)
{
if (strcmp(element_name, "plugin_info")==0) {
plugininfo_ParseUserData *Data = (plugininfo_ParseUserData *)user_data;
Data->info_str = g_markup_printf_escaped("<b>%s</b> %s\n%s", Data->name.c_str(), Data->version.c_str(), Data->short_desc.c_str());
Data->detail_str = g_markup_printf_escaped(_("%s\n\n<b>Author:</b>\t%s\n<b>Website:</b>\t%s\n<b>Filename:</b>\t%s"), Data->long_desc.c_str(), Data->author.c_str(), Data->website.c_str(), Data->filename.c_str());
}
}
static void plugininfo_parse_text(GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error)
{
const gchar *element = g_markup_parse_context_get_element(context);
if (!element)
return;
plugininfo_ParseUserData *Data = (plugininfo_ParseUserData *)user_data;
if (strcmp(element, "name")==0) {
Data->name.assign(text, text_len);
} else if (strcmp(element, "version")==0) {
Data->version.assign(text, text_len);
} else if (strcmp(element, "short_desc")==0) {
Data->short_desc.assign(text, text_len);
} else if (strcmp(element, "long_desc")==0) {
Data->long_desc.assign(text, text_len);
} else if (strcmp(element, "author")==0) {
Data->author.assign(text, text_len);
} else if (strcmp(element, "website")==0) {
Data->website.assign(text, text_len);
}
}
static void add_tree_model(GtkTreeStore *tree_model, GtkTreeIter*parent, const std::list<StarDictPluginInfo> &infolist)
{
plugininfo_ParseUserData Data;
GMarkupParser parser;
parser.start_element = plugininfo_parse_start_element;
parser.end_element = plugininfo_parse_end_element;
parser.text = plugininfo_parse_text;
parser.passthrough = NULL;
parser.error = NULL;
GtkTreeIter iter;
for (std::list<StarDictPluginInfo>::const_iterator i = infolist.begin(); i != infolist.end(); ++i) {
Data.info_str = NULL;
Data.detail_str = NULL;
Data.filename = i->filename;
GMarkupParseContext* context = g_markup_parse_context_new(&parser, (GMarkupParseFlags)0, &Data, NULL);
g_markup_parse_context_parse(context, i->info_xml.c_str(), -1, NULL);
g_markup_parse_context_end_parse(context, NULL);
g_markup_parse_context_free(context);
gtk_tree_store_append(tree_model, &iter, parent);
bool loaded = gpAppFrame->oStarDictPlugins->get_loaded(i->filename.c_str());
gtk_tree_store_set(tree_model, &iter, 0, true, 1, loaded, 2, Data.info_str, 3, Data.detail_str, 4, i->filename.c_str(), 5, i->plugin_type, 6, i->can_configure, -1);
g_free(Data.info_str);
g_free(Data.detail_str);
}
}
static void init_tree_model(GtkTreeStore *tree_model)
{
std::list<std::pair<StarDictPlugInType, std::list<StarDictPluginInfo> > > plugin_list;
{
#ifdef _WIN32
std::list<std::string> plugin_order_list;
const std::list<std::string>& plugin_order_list_rel
= conf->get_strlist("/apps/stardict/manage_plugins/plugin_order_list");
abs_path_to_data_dir(plugin_order_list_rel, plugin_order_list);
#else
const std::list<std::string>& plugin_order_list
= conf->get_strlist("/apps/stardict/manage_plugins/plugin_order_list");
#endif
gpAppFrame->oStarDictPlugins->get_plugin_list(plugin_order_list, plugin_list);
}
GtkTreeIter iter;
for (std::list<std::pair<StarDictPlugInType, std::list<StarDictPluginInfo> > >::iterator i = plugin_list.begin(); i != plugin_list.end(); ++i) {
switch (i->first) {
case StarDictPlugInType_VIRTUALDICT:
gtk_tree_store_append(tree_model, &iter, NULL);
gtk_tree_store_set(tree_model, &iter, 0, false, 2, _("<b>Virtual Dictionary</b>"), -1);
add_tree_model(tree_model, &iter, i->second);
break;
case StarDictPlugInType_VIRTUALDICT2:
gtk_tree_store_append(tree_model, &iter, NULL);
gtk_tree_store_set(tree_model, &iter, 0, false, 2, _("<b>Virtual Dictionary 2</b>"), -1);
add_tree_model(tree_model, &iter, i->second);
break;
case StarDictPlugInType_NETDICT:
gtk_tree_store_append(tree_model, &iter, NULL);
gtk_tree_store_set(tree_model, &iter, 0, false, 2, _("<b>Network Dictionary</b>"), -1);
add_tree_model(tree_model, &iter, i->second);
break;
case StarDictPlugInType_SPECIALDICT:
gtk_tree_store_append(tree_model, &iter, NULL);
gtk_tree_store_set(tree_model, &iter, 0, false, 2, _("<b>Special Dictionary</b>"), -1);
add_tree_model(tree_model, &iter, i->second);
break;
case StarDictPlugInType_TTS:
gtk_tree_store_append(tree_model, &iter, NULL);
gtk_tree_store_set(tree_model, &iter, 0, false, 2, _("<b>TTS Engine</b>"), -1);
add_tree_model(tree_model, &iter, i->second);
break;
case StarDictPlugInType_PARSEDATA:
gtk_tree_store_append(tree_model, &iter, NULL);
gtk_tree_store_set(tree_model, &iter, 0, false, 2, _("<b>Data Parsing Engine</b>"), -1);
add_tree_model(tree_model, &iter, i->second);
break;
case StarDictPlugInType_MISC:
gtk_tree_store_append(tree_model, &iter, NULL);
gtk_tree_store_set(tree_model, &iter, 0, false, 2, _("<b>Misc</b>"), -1);
add_tree_model(tree_model, &iter, i->second);
break;
default:
break;
}
}
}
void PluginManageDlg::on_plugin_treeview_selection_changed(GtkTreeSelection *selection, PluginManageDlg *oPluginManageDlg)
{
GtkTreeModel *model;
GtkTreeIter iter;
if (! gtk_tree_selection_get_selected (selection, &model, &iter))
return;
if (gtk_tree_model_iter_has_child(model, &iter)) {
gtk_widget_set_sensitive(oPluginManageDlg->pref_button, FALSE);
} else {
gboolean loaded;
gchar *detail;
gboolean can_configure;
gtk_tree_model_get (model, &iter, 1, &loaded, 3, &detail, 6, &can_configure, -1);
gtk_label_set_markup(GTK_LABEL(oPluginManageDlg->detail_label), detail);
g_free(detail);
if (loaded)
gtk_widget_set_sensitive(oPluginManageDlg->pref_button, can_configure);
else
gtk_widget_set_sensitive(oPluginManageDlg->pref_button, FALSE);
}
}
gboolean PluginManageDlg::on_treeview_button_press(GtkWidget * widget, GdkEventButton * event, PluginManageDlg *oPluginManageDlg)
{
if (event->type==GDK_2BUTTON_PRESS) {
if (gtk_widget_get_sensitive(GTK_WIDGET(oPluginManageDlg->pref_button)))
gtk_dialog_response(GTK_DIALOG(oPluginManageDlg->window), STARDICT_RESPONSE_CONFIGURE);
return true;
} else {
return false;
}
}
static void add_order_list(std::list<std::string> &order_list, GtkTreeModel *now_tree_model, GtkTreeIter *parent)
{
gboolean have_iter;
GtkTreeIter iter;
have_iter = gtk_tree_model_iter_children(now_tree_model, &iter, parent);
gchar *filename;
while (have_iter) {
gtk_tree_model_get (now_tree_model, &iter, 4, &filename, -1);
order_list.push_back(filename);
g_free(filename);
have_iter = gtk_tree_model_iter_next(now_tree_model, &iter);
}
}
void PluginManageDlg::write_order_list()
{
std::list<std::string> order_list;
GtkTreeModel *now_tree_model = GTK_TREE_MODEL(plugin_tree_model);
gboolean have_iter;
GtkTreeIter iter;
have_iter = gtk_tree_model_get_iter_first(now_tree_model, &iter);
while (have_iter) {
if (gtk_tree_model_iter_has_child(now_tree_model, &iter)) {
add_order_list(order_list, now_tree_model, &iter);
}
have_iter = gtk_tree_model_iter_next(now_tree_model, &iter);
}
#ifdef _WIN32
{
std::list<std::string> order_list_rel;
rel_path_to_data_dir(order_list, order_list_rel);
std::swap(order_list, order_list_rel);
}
#endif
conf->set_strlist("/apps/stardict/manage_plugins/plugin_order_list", order_list);
}
void PluginManageDlg::drag_data_get_cb(GtkWidget *widget, GdkDragContext *ctx, GtkSelectionData *data, guint info, guint time, PluginManageDlg *oPluginManageDlg)
{
if (gtk_selection_data_get_target(data) == gdk_atom_intern("STARDICT_PLUGINMANAGE", FALSE)) {
GtkTreeRowReference *ref;
GtkTreePath *source_row;
ref = (GtkTreeRowReference *)g_object_get_data(G_OBJECT(ctx), "gtk-tree-view-source-row");
source_row = gtk_tree_row_reference_get_path(ref);
if (source_row == NULL)
return;
GtkTreeIter iter;
gtk_tree_model_get_iter(GTK_TREE_MODEL(oPluginManageDlg->plugin_tree_model), &iter, source_row);
gtk_selection_data_set(data, gdk_atom_intern("STARDICT_PLUGINMANAGE", FALSE), 8, (const guchar *)&iter, sizeof(iter));
gtk_tree_path_free(source_row);
}
}
void PluginManageDlg::drag_data_received_cb(GtkWidget *widget, GdkDragContext *ctx, guint x, guint y, GtkSelectionData *sd, guint info, guint t, PluginManageDlg *oPluginManageDlg)
{
if (gtk_selection_data_get_target(sd) == gdk_atom_intern("STARDICT_PLUGINMANAGE", FALSE) && gtk_selection_data_get_data(sd)) {
GtkTreePath *path = NULL;
GtkTreeViewDropPosition position;
GtkTreeIter drag_iter;
memcpy(&drag_iter, gtk_selection_data_get_data(sd), sizeof(drag_iter));
if (gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), x, y, &path, &position)) {
GtkTreeIter iter;
GtkTreeModel *model = GTK_TREE_MODEL(oPluginManageDlg->plugin_tree_model);
gtk_tree_model_get_iter(model, &iter, path);
if (gtk_tree_model_iter_has_child(model, &iter)) {
gtk_drag_finish (ctx, FALSE, FALSE, t);
return;
}
if (gtk_tree_model_iter_has_child(model, &drag_iter)) {
gtk_drag_finish (ctx, FALSE, FALSE, t);
return;
}
GtkTreeIter parent_iter;
if (!gtk_tree_model_iter_parent(model, &parent_iter, &iter)) {
gtk_drag_finish (ctx, FALSE, FALSE, t);
return;
}
GtkTreeIter drag_parent_iter;
if (!gtk_tree_model_iter_parent(model, &drag_parent_iter, &drag_iter)) {
gtk_drag_finish (ctx, FALSE, FALSE, t);
return;
}
char *iter_str, *drag_iter_str;
iter_str = gtk_tree_model_get_string_from_iter(model, &parent_iter);
drag_iter_str = gtk_tree_model_get_string_from_iter(model, &drag_parent_iter);
if (strcmp(iter_str, drag_iter_str) != 0) {
g_free(iter_str);
g_free(drag_iter_str);
gtk_drag_finish (ctx, FALSE, FALSE, t);
return;
}
g_free(iter_str);
g_free(drag_iter_str);
switch (position) {
case GTK_TREE_VIEW_DROP_AFTER:
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
gtk_tree_store_move_after(GTK_TREE_STORE(model), &drag_iter, &iter);
break;
case GTK_TREE_VIEW_DROP_BEFORE:
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
gtk_tree_store_move_before(GTK_TREE_STORE(model), &drag_iter, &iter);
break;
default: {
gtk_drag_finish (ctx, FALSE, FALSE, t);
return;
}
}
oPluginManageDlg->write_order_list();
oPluginManageDlg->order_changed_ = true;
gtk_drag_finish (ctx, TRUE, FALSE, t);
}
}
}
GtkWidget *PluginManageDlg::create_plugin_list()
{
GtkWidget *sw;
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
plugin_tree_model = gtk_tree_store_new(7, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
init_tree_model(plugin_tree_model);
treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL(plugin_tree_model));
g_object_unref (G_OBJECT (plugin_tree_model));
gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
g_signal_connect (G_OBJECT (treeview), "button_press_event", G_CALLBACK (on_treeview_button_press), this);
GtkTreeSelection *selection;
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
g_signal_connect (selection, "changed", G_CALLBACK (on_plugin_treeview_selection_changed), this);
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
renderer = gtk_cell_renderer_toggle_new ();
g_signal_connect (renderer, "toggled", G_CALLBACK (on_plugin_enable_toggled), this);
column = gtk_tree_view_column_new_with_attributes (_("Enable"), renderer, "visible", 0, "active", 1, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
gtk_tree_view_column_set_expand(GTK_TREE_VIEW_COLUMN (column), FALSE);
gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), FALSE);
renderer = gtk_cell_renderer_text_new ();
g_object_set (G_OBJECT (renderer), "xalign", 0.0, NULL);
column = gtk_tree_view_column_new_with_attributes (_("Plug-in Name"), renderer, "markup", 2, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
gtk_tree_view_column_set_expand(GTK_TREE_VIEW_COLUMN (column), TRUE);
gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), FALSE);
GtkTargetEntry gte[] = {{(gchar *)"STARDICT_PLUGINMANAGE", GTK_TARGET_SAME_APP, 0}};
gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(treeview), GDK_BUTTON1_MASK, gte, 1, GDK_ACTION_COPY);
gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(treeview), gte, 1, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE));
g_signal_connect(G_OBJECT(treeview), "drag-data-received", G_CALLBACK(drag_data_received_cb), this);
g_signal_connect(G_OBJECT(treeview), "drag-data-get", G_CALLBACK(drag_data_get_cb), this);
gtk_tree_view_expand_all(GTK_TREE_VIEW (treeview));
gtk_container_add (GTK_CONTAINER (sw), treeview);
return sw;
}
bool PluginManageDlg::ShowModal(GtkWindow *parent_win, bool &dict_changed, bool &order_changed)
{
window = gtk_dialog_new();
oStarDictPluginSystemInfo.pluginwin = window;
gtk_window_set_transient_for(GTK_WINDOW(window), parent_win);
gtk_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_HELP, GTK_RESPONSE_HELP);
pref_button = gtk_dialog_add_button(GTK_DIALOG(window), _("Configure Pl_ug-in"), STARDICT_RESPONSE_CONFIGURE);
gtk_widget_set_sensitive(pref_button, FALSE);
gtk_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
gtk_dialog_set_default_response(GTK_DIALOG(window), GTK_RESPONSE_CLOSE);
g_signal_connect(G_OBJECT(window), "response", G_CALLBACK(response_handler), this);
GtkWidget *vbox;
#if GTK_MAJOR_VERSION >= 3
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
#else
vbox = gtk_vbox_new (FALSE, 5);
#endif
gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
GtkWidget *pluginlist = create_plugin_list();
gtk_box_pack_start (GTK_BOX (vbox), pluginlist, true, true, 0);
GtkWidget *expander = gtk_expander_new (_("<b>Plug-in Details</b>"));
gtk_expander_set_use_markup(GTK_EXPANDER(expander), TRUE);
gtk_box_pack_start (GTK_BOX (vbox), expander, false, false, 0);
detail_label = gtk_label_new (NULL);
gtk_label_set_line_wrap(GTK_LABEL(detail_label), TRUE);
gtk_label_set_selectable(GTK_LABEL (detail_label), TRUE);
gtk_container_add (GTK_CONTAINER (expander), detail_label);
gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG (window))), vbox, true, true, 0);
gtk_widget_show_all (gtk_dialog_get_content_area(GTK_DIALOG (window)));
gtk_window_set_title (GTK_WINDOW (window), _("Manage Plugins"));
gtk_window_set_default_size(GTK_WINDOW(window), 250, 350);
dict_changed_ = false;
order_changed_ = false;
gint result;
while (true) {
result = gtk_dialog_run(GTK_DIALOG(window));
if (result ==GTK_RESPONSE_HELP || result == STARDICT_RESPONSE_CONFIGURE) {
} else {
break;
}
}
if (result != GTK_RESPONSE_NONE) {
dict_changed = dict_changed_;
order_changed = order_changed_;
gtk_widget_destroy(GTK_WIDGET(window));
}
window = NULL;
treeview = NULL;
detail_label = NULL;
pref_button = NULL;
plugin_tree_model = NULL;
oStarDictPluginSystemInfo.pluginwin = NULL;
return result == GTK_RESPONSE_NONE;
}