dialog-plugin-manager.c 25.1 KB
Newer Older
1
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 3 4 5
/*
 * plugin-manager.c: Dialog used to load plugins into the Gnumeric
 * spreadsheet
 *
Chyla Zbigniew's avatar
Chyla Zbigniew committed
6 7
 * Authors:
 *  Old plugin manager:
8
 *   Dom Lachowicz (dominicl@seas.upenn.edu)
9
 *   Tom Dyas (tdyas@romulus.rutgers.edu)
Chyla Zbigniew's avatar
Chyla Zbigniew committed
10 11
 *  New plugin manager:
 *   Zbigniew Chyla (cyba@gnome.pl)
12
 *   Andreas J. Guelzow (aguelzow@taliesin.ca)
jpekka's avatar
jpekka committed
13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *
 * 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.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27
 */
28

29
#include <gnumeric-config.h>
30
#include <glib/gi18n.h>
31 32
#include <gnumeric.h>
#include "dialogs.h"
33
#include "help.h"
34

35
#include <gutils.h>
36 37 38 39 40
#include <gui-util.h>
#include <command-context.h>
#include <workbook-control.h>
#include <workbook.h>
#include <plugin.h>
41
#include <plugin-service.h>
42 43
#include <gnumeric-gconf.h>
#include <application.h>
44

45
#include <glade/glade.h>
46 47 48 49 50 51 52 53
#include <gtk/gtknotebook.h>
#include <gtk/gtktreeview.h>
#include <gtk/gtktreestore.h>
#include <gtk/gtktreeselection.h>
#include <gtk/gtkcellrenderertoggle.h>
#include <gtk/gtkcellrenderertext.h>
#include <gtk/gtkliststore.h>
#include <gtk/gtkcheckbutton.h>
54 55
#include <gtk/gtkfilechooserdialog.h>
#include <gtk/gtkstock.h>
Jody Goldberg's avatar
Jody Goldberg committed
56 57 58
#include <gsf/gsf-impl-utils.h>

#include <string.h>
59

60 61
#define PLUGIN_MANAGER_DIALOG_KEY "zoom-dialog"

Chyla Zbigniew's avatar
Chyla Zbigniew committed
62 63
typedef struct {
	WorkbookControlGUI *wbcg;
64
	GladeXML *gui;
Jody Goldberg's avatar
Jody Goldberg committed
65
	GtkDialog *dialog_pm;
66
	GtkNotebook *gnotebook;
67 68
	GtkListStore  *model_plugins;
	GtkTreeView   *list_plugins;
69 70
	GtkTreeStore  *model_details;
	GtkTreeView   *view_details;
71
	GtkTreeSelection   *selection;
72
	GtkButton *button_rescan_directories;
73
	GtkButton *button_directory_add, *button_directory_delete;
74
	GtkButton *button_activate_all, *button_deactivate_all;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
75
	GtkCheckButton *checkbutton_install_new;
76 77
	GtkWidget *frame_mark_for_deactivation;
	GtkWidget *checkbutton_mark_for_deactivation;
78
	GtkEntry *entry_directory;
79
	GtkTextBuffer *text_description;
80 81 82
	GtkListStore  *model_directories;
	GtkTreeView   *list_directories;
	GtkTreeSelection   *selection_directory;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
83 84
} PluginManagerGUI;

85 86
enum {
	PLUGIN_NAME,
87 88
	PLUGIN_ACTIVE,
	PLUGIN_SWITCHABLE,
89 90 91
	PLUGIN_POINTER,
	NUM_COLMNS
};
92 93 94 95 96
enum {
	DIR_NAME,
	DIR_IS_SYSTEM,
	DIR_NUM_COLMNS
};
97 98 99 100 101
enum {
	DETAILS_DESC,
	DETAILS_ID,
	DETAILS_NUM_COLMNS
};
102

Chyla Zbigniew's avatar
Chyla Zbigniew committed
103

104 105 106 107 108
static int
plugin_compare_name (gconstpointer a, gconstpointer b)
{
	GnmPlugin *plugin_a = (GnmPlugin *) a, *plugin_b = (GnmPlugin *) b;

109 110
	return g_utf8_collate (gnm_plugin_get_name (plugin_a),
			       gnm_plugin_get_name (plugin_b));
111 112
}

113 114
static gboolean
model_get_plugin_iter (GtkTreeModel *model, gpointer plugin, GtkTreeIter *ret_iter)
115
{
116
	gboolean has_iter;
Morten Welinder's avatar
Morten Welinder committed
117

118 119 120
	for (has_iter = gtk_tree_model_get_iter_first (model, ret_iter);
	     has_iter; has_iter = gtk_tree_model_iter_next (model, ret_iter)) {
		gpointer current;
Morten Welinder's avatar
Morten Welinder committed
121

122 123
		gtk_tree_model_get (model, ret_iter, PLUGIN_POINTER, &current, -1);
		if (current == plugin) {
124
			return TRUE;
125 126 127
		}
	}

128
	return FALSE;
129 130 131
}

static void
132
cb_plugin_changed (GnmPlugin *plugin, PluginManagerGUI *pm_gui)
133
{
134
	GtkTreeIter iter;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
135

136 137 138 139 140 141
	if (model_get_plugin_iter (GTK_TREE_MODEL (pm_gui->model_plugins), plugin, &iter)) {
		gtk_list_store_set (
			pm_gui->model_plugins, &iter,
			PLUGIN_ACTIVE, gnm_plugin_is_active (plugin),
			PLUGIN_SWITCHABLE, !gnm_plugin_is_active (plugin) || gnm_plugin_can_deactivate (plugin),
			-1);
Chyla Zbigniew's avatar
Chyla Zbigniew committed
142
	}
Jody Goldberg's avatar
Jody Goldberg committed
143 144
}

145
static void
146
cb_plugin_destroyed (PluginManagerGUI *pm_gui, GObject *ex_plugin)
147
{
148
	GtkTreeIter iter;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
149

150 151
	if (model_get_plugin_iter (GTK_TREE_MODEL (pm_gui->model_plugins), ex_plugin, &iter)) {
		gtk_list_store_remove (pm_gui->model_plugins, &iter);
152
	}
Chyla Zbigniew's avatar
Chyla Zbigniew committed
153
}
154

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
static void
set_plugin_model_row (PluginManagerGUI *pm_gui, GtkTreeIter *iter, GnmPlugin *plugin)
{
	gtk_list_store_set (
		pm_gui->model_plugins, iter,
		PLUGIN_NAME,  gnm_plugin_get_name (plugin),
		PLUGIN_ACTIVE, gnm_plugin_is_active (plugin),
		PLUGIN_SWITCHABLE, !gnm_plugin_is_active (plugin) || gnm_plugin_can_deactivate (plugin),
		PLUGIN_POINTER, plugin,
		-1);
	g_signal_connect (
		G_OBJECT (plugin), "state_changed",
		G_CALLBACK (cb_plugin_changed), pm_gui);
	g_signal_connect (
		G_OBJECT (plugin), "can_deactivate_changed",
		G_CALLBACK (cb_plugin_changed), pm_gui);
	g_object_weak_ref (
		G_OBJECT (plugin), (GWeakNotify) cb_plugin_destroyed, pm_gui);
}

Chyla Zbigniew's avatar
Chyla Zbigniew committed
175
static void
176
cb_pm_button_rescan_directories_clicked (PluginManagerGUI *pm_gui)
Chyla Zbigniew's avatar
Chyla Zbigniew committed
177
{
178
	ErrorInfo *error;
179 180 181 182
	GSList *new_plugins, *l;
	GtkTreeModel *model = GTK_TREE_MODEL (pm_gui->model_plugins);
	GtkTreeIter iter, new_iter;
	gboolean has_iter;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
183

184
	plugins_rescan (&error, &new_plugins);
Chyla Zbigniew's avatar
Chyla Zbigniew committed
185
	if (error != NULL) {
Jody Goldberg's avatar
Jody Goldberg committed
186
		gnm_cmd_context_error_info (GNM_CMD_CONTEXT (pm_gui->wbcg), error);
Chyla Zbigniew's avatar
Chyla Zbigniew committed
187 188
		error_info_free (error);
	}
189 190 191 192 193
	GNM_SLIST_SORT (new_plugins, plugin_compare_name);
	for (has_iter = gtk_tree_model_get_iter_first (model, &iter), l = new_plugins;
	     has_iter && l != NULL;
	     has_iter = gtk_tree_model_iter_next (model, &iter)) {
		GnmPlugin *old_plugin, *new_plugin;
Morten Welinder's avatar
Morten Welinder committed
194

195 196 197 198 199 200 201 202 203 204 205 206 207 208
		gtk_tree_model_get (model, &iter, PLUGIN_POINTER, &old_plugin, -1);
		while (new_plugin = l->data, plugin_compare_name (old_plugin, new_plugin) > 0) {
			gtk_list_store_insert_before (pm_gui->model_plugins, &new_iter, &iter);
			set_plugin_model_row (pm_gui, &new_iter, new_plugin);
			l = l->next;
			if (l == NULL)
				break;
		}
	}
	while (l != NULL) {
		gtk_list_store_append (pm_gui->model_plugins, &new_iter);
		set_plugin_model_row (pm_gui, &new_iter, GNM_PLUGIN (l->data));
		l = l->next;
	}
209
	g_slist_free (new_plugins);
Chyla Zbigniew's avatar
Chyla Zbigniew committed
210
}
211

Chyla Zbigniew's avatar
Chyla Zbigniew committed
212
static void
J.H.M. Dassen (Ray)'s avatar
J.H.M. Dassen (Ray) committed
213
cb_pm_checkbutton_install_new_toggled (GtkCheckButton *checkbutton,
Morten Welinder's avatar
Morten Welinder committed
214
				       G_GNUC_UNUSED PluginManagerGUI *pm_gui)
Chyla Zbigniew's avatar
Chyla Zbigniew committed
215
{
216 217
	gnm_gconf_set_activate_new_plugins (
		gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbutton)));
Chyla Zbigniew's avatar
Chyla Zbigniew committed
218
}
219

220
static void
221 222
pm_gui_load_directories (PluginManagerGUI *pm_gui,
			 GSList const *plugin_dirs, gboolean is_conf)
223
{
224 225 226 227 228 229 230
	for (; plugin_dirs; plugin_dirs = plugin_dirs->next) {
		GtkTreeIter iter;
		gtk_list_store_append (pm_gui->model_directories, &iter);
		gtk_list_store_set (pm_gui->model_directories, &iter,
				    DIR_NAME, (char *) plugin_dirs->data,
				    DIR_IS_SYSTEM, !is_conf,
				    -1);
231 232 233 234
	}
}

static void
235
pm_gui_load_directory_page (PluginManagerGUI *pm_gui)
236
{
237 238 239
	GtkTreeIter iter;
	char * sys_plugins = gnumeric_sys_plugin_dir ();
	char * usr_plugins = gnumeric_usr_plugin_dir ();
240
	GSList *plugin_dirs;
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
	gchar const *plugin_path_env;

	gtk_list_store_clear (pm_gui->model_directories);

	gtk_list_store_append (pm_gui->model_directories, &iter);
	gtk_list_store_set (pm_gui->model_directories, &iter,
			    DIR_NAME, sys_plugins,
			    DIR_IS_SYSTEM, TRUE,
			    -1);
	g_free (sys_plugins);
	gtk_list_store_append (pm_gui->model_directories, &iter);
	gtk_list_store_set (pm_gui->model_directories, &iter,
			    DIR_NAME, usr_plugins,
			    DIR_IS_SYSTEM, TRUE,
			    -1);
	g_free (usr_plugins);
Morten Welinder's avatar
Morten Welinder committed
257

258 259 260 261
	plugin_path_env = g_getenv ("GNUMERIC_PLUGIN_PATH");
	if (plugin_path_env != NULL) {
		plugin_dirs = g_strsplit_to_slist (plugin_path_env, ":");
		pm_gui_load_directories (pm_gui, plugin_dirs, FALSE);
Jody Goldberg's avatar
Jody Goldberg committed
262 263
		g_slist_foreach (plugin_dirs, (GFunc)g_free, NULL);
		g_slist_free (plugin_dirs);
264
	}
265
	pm_gui_load_directories (pm_gui, gnm_app_prefs->plugin_extra_dirs, TRUE);
266 267 268
}

static void
Morten Welinder's avatar
Morten Welinder committed
269
cb_pm_button_directory_add_clicked (G_GNUC_UNUSED GtkButton *button,
270
				    PluginManagerGUI *pm_gui)
271
{
272 273 274 275
	GtkFileChooser *fsel;

	fsel = GTK_FILE_CHOOSER
		(g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG,
276
			       "action", GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
			       "title", _("Select Directory"),
			       /* We need to force local-only as plugins
				  won't work over the network.  */
			       "local-only", TRUE,
			       NULL));
	gtk_dialog_add_buttons (GTK_DIALOG (fsel),
				GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
				GTK_STOCK_ADD, GTK_RESPONSE_OK,
				NULL);
	gtk_dialog_set_default_response (GTK_DIALOG (fsel), GTK_RESPONSE_OK);

	if (gnumeric_dialog_file_selection (pm_gui->wbcg, GTK_WIDGET (fsel))) {
		char *path = gtk_file_chooser_get_filename (fsel);

		if (!g_file_test (path, G_FILE_TEST_IS_DIR)) {
			char *dir_name = g_path_get_dirname (path);
			g_free (path);
			path = dir_name;
		}
296 297

		if (g_slist_find_custom ((GSList *)gnm_app_prefs->plugin_extra_dirs,
298
					 path, g_str_compare) == NULL) {
299 300
			GSList *extra_dirs = g_string_slist_copy (
				gnm_app_prefs->plugin_extra_dirs);
301
			GNM_SLIST_PREPEND (extra_dirs, path);
302 303 304 305 306 307
			GNM_SLIST_SORT (extra_dirs, g_str_compare);

			gnm_gconf_set_plugin_extra_dirs (extra_dirs);
			pm_gui_load_directory_page (pm_gui);
			cb_pm_button_rescan_directories_clicked (pm_gui);
		} else
308
			g_free (path);
309
	}
310 311

	gtk_widget_destroy (GTK_WIDGET (fsel));
312 313 314
}

static void
Morten Welinder's avatar
Morten Welinder committed
315
cb_pm_button_directory_delete_clicked (G_GNUC_UNUSED GtkButton *button,
316
				       PluginManagerGUI *pm_gui)
317 318
{
	GtkTreeIter iter;
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
	char     *dir_name = NULL;
	gboolean  is_system = TRUE;

	if (!gtk_tree_selection_get_selected (pm_gui->selection_directory, NULL, &iter))
		return;

	gtk_tree_model_get (GTK_TREE_MODEL (pm_gui->model_directories), &iter,
			    DIR_NAME,		&dir_name,
			    DIR_IS_SYSTEM,	&is_system,
			    -1);

	if (!is_system
	    && g_slist_find_custom ((GSList *)gnm_app_prefs->plugin_extra_dirs,
				    dir_name, g_str_compare) != NULL) {

		GSList *extra_dirs = g_string_slist_copy (gnm_app_prefs->plugin_extra_dirs);
		GSList *res = g_slist_find_custom (extra_dirs, dir_name, g_str_compare);

		g_free (res->data);
		extra_dirs = g_slist_remove (extra_dirs, res->data);

		gnm_gconf_set_plugin_extra_dirs (extra_dirs);
		pm_gui_load_directory_page (pm_gui);
		cb_pm_button_rescan_directories_clicked (pm_gui);
343
	}
344
	g_free (dir_name);
345 346
}

347 348 349 350 351 352 353
static void
cb_checkbutton_mark_for_deactivation_toggled (GtkCheckButton *cbtn, GnmPlugin *plugin)
{
	plugin_db_mark_plugin_for_deactivation (
		plugin, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (cbtn)));
}

Chyla Zbigniew's avatar
Chyla Zbigniew committed
354
static void
355
cb_pm_selection_changed (GtkTreeSelection *selection, PluginManagerGUI *pm_gui)
Chyla Zbigniew's avatar
Chyla Zbigniew committed
356
{
357
	GnmPlugin *pinfo;
358
	GtkTreeIter iter;
359
	const char *plugin_desc;
360 361 362

	g_return_if_fail (pm_gui != NULL);

363 364 365 366 367
	g_signal_handlers_disconnect_matched (
		pm_gui->checkbutton_mark_for_deactivation,
		G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
		cb_checkbutton_mark_for_deactivation_toggled, NULL);

368
	if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
369
		gtk_text_buffer_set_text (pm_gui->text_description, "", 0);
370 371
		gtk_entry_set_text (pm_gui->entry_directory, "");
		gtk_tree_store_clear (pm_gui->model_details);
372
		gtk_widget_hide (pm_gui->frame_mark_for_deactivation);
Chyla Zbigniew's avatar
Chyla Zbigniew committed
373
	} else {
374 375 376
		GtkTreeIter iter2, iter3;
		GSList *dep_ids, *services;

377 378 379 380 381 382 383 384
		gtk_tree_model_get (GTK_TREE_MODEL (pm_gui->model_plugins),
		                    &iter, PLUGIN_POINTER, &pinfo, -1);
		plugin_desc = gnm_plugin_get_description (pinfo);
		if (plugin_desc == NULL) {
			plugin_desc = "";
		}
		gtk_text_buffer_set_text (
			pm_gui->text_description, plugin_desc, strlen (plugin_desc));
385
		gtk_entry_set_text (pm_gui->entry_directory, gnm_plugin_get_dir_name (pinfo));
386

387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
		gtk_tree_store_clear (pm_gui->model_details);
		gtk_tree_store_append (pm_gui->model_details, &iter, NULL);
		gtk_tree_store_set (
			pm_gui->model_details, &iter,
			DETAILS_DESC, gnm_plugin_get_name (pinfo),
			DETAILS_ID, gnm_plugin_get_id (pinfo),
			-1);
		dep_ids = gnm_plugin_get_dependencies_ids (pinfo);
		if (dep_ids != NULL) {
			gtk_tree_store_append (pm_gui->model_details, &iter2, &iter);
			gtk_tree_store_set (
				pm_gui->model_details, &iter2,
				DETAILS_DESC, _("Plugin dependencies"),
				DETAILS_ID, "",
				-1);
			GNM_SLIST_FOREACH (dep_ids, char, dep_id,
				GnmPlugin *dep_plugin;
				const char *name;

				dep_plugin = plugins_get_plugin_by_id (dep_id);
				name =  dep_plugin != NULL ? (char *) gnm_plugin_get_name (dep_plugin) : _("Unknown plugin");
				gtk_tree_store_append (pm_gui->model_details, &iter3, &iter2);
				gtk_tree_store_set (
					pm_gui->model_details, &iter3,
					DETAILS_DESC, name,
					DETAILS_ID, dep_id,
					-1);
			);
415
		}
416 417 418 419 420 421 422 423 424
		g_slist_free_custom (dep_ids, g_free);

		gtk_tree_store_append (pm_gui->model_details, &iter2, &iter);
		gtk_tree_store_set (
			pm_gui->model_details, &iter2,
			DETAILS_DESC, _("Plugin services"),
			DETAILS_ID, "",
			-1);
		services = gnm_plugin_get_services (pinfo);
Jody Goldberg's avatar
Jody Goldberg committed
425
		GNM_SLIST_FOREACH (services, GnmPluginService, service,
426 427 428 429 430 431 432 433
			gtk_tree_store_append (pm_gui->model_details, &iter3, &iter2);
			gtk_tree_store_set (
				pm_gui->model_details, &iter3,
				DETAILS_DESC, plugin_service_get_description (service),
				DETAILS_ID, plugin_service_get_id (service),
				-1);
		);
		gtk_tree_view_expand_all (pm_gui->view_details);
434 435 436 437 438 439 440 441 442 443 444 445 446

		if (gnm_plugin_is_active (pinfo) && !gnm_plugin_can_deactivate (pinfo)) {
			gtk_toggle_button_set_active (
				GTK_TOGGLE_BUTTON (pm_gui->checkbutton_mark_for_deactivation),
				plugin_db_is_plugin_marked_for_deactivation (pinfo));
			g_signal_connect (
				pm_gui->checkbutton_mark_for_deactivation, "toggled",
				G_CALLBACK (cb_checkbutton_mark_for_deactivation_toggled),
				pinfo);
			gtk_widget_show (pm_gui->frame_mark_for_deactivation);
		} else {
			gtk_widget_hide (pm_gui->frame_mark_for_deactivation);
		}
447 448
	}
}
449

450
static void
Morten Welinder's avatar
Morten Welinder committed
451
pm_dialog_cleanup (G_GNUC_UNUSED GObject *dialog,
452
		   PluginManagerGUI *pm_gui)
453 454
{
	GtkTreeModel *model = GTK_TREE_MODEL (pm_gui->model_plugins);
455 456
	GtkTreeIter iter;
	gboolean has_iter;
Morten Welinder's avatar
Morten Welinder committed
457

458 459
	for (has_iter = gtk_tree_model_get_iter_first (model, &iter);
	     has_iter; has_iter = gtk_tree_model_iter_next (model, &iter)) {
460
		gpointer plugin;
Morten Welinder's avatar
Morten Welinder committed
461

462 463 464 465 466 467 468 469
		gtk_tree_model_get (model, &iter, PLUGIN_POINTER, &plugin, -1);
		g_signal_handlers_disconnect_by_func (
			G_OBJECT (plugin), G_CALLBACK (cb_plugin_changed), pm_gui);
		g_signal_handlers_disconnect_by_func (
			G_OBJECT (plugin), G_CALLBACK (cb_plugin_changed), pm_gui);
		g_object_weak_unref (
			G_OBJECT (plugin), (GWeakNotify) cb_plugin_destroyed, pm_gui);
	}
470 471 472 473 474 475 476 477

	if (pm_gui->gui != NULL) {
		g_object_unref (G_OBJECT (pm_gui->gui));
		pm_gui->gui = NULL;
	}

	pm_gui->dialog_pm = NULL;
	g_free (pm_gui);
478
}
479

480
static void
Morten Welinder's avatar
Morten Welinder committed
481
cb_pm_button_activate_all_clicked (G_GNUC_UNUSED GtkButton *button,
482
				   PluginManagerGUI *pm_gui)
483 484 485 486 487 488 489 490
{
	ErrorInfo *activation_error, *error;

	plugin_db_activate_plugin_list (
		plugins_get_available_plugins (), &activation_error);
	if (activation_error != NULL) {
		error = error_info_new_str_with_details (
			_("Errors while activating plugins"), activation_error);
491 492
		gnumeric_error_info_dialog_show (
			GTK_WINDOW (pm_gui->dialog_pm), error);
493 494 495 496 497
		error_info_free (error);
	}
}

static void
Morten Welinder's avatar
Morten Welinder committed
498
cb_pm_button_deactivate_all_clicked (G_GNUC_UNUSED GtkButton *button,
499
				     PluginManagerGUI *pm_gui)
500 501 502 503 504 505 506 507
{
	ErrorInfo *deactivation_error, *error;

	plugin_db_deactivate_plugin_list (
		plugins_get_available_plugins (), &deactivation_error);
	if (deactivation_error != NULL) {
		error = error_info_new_str_with_details (
			_("Errors while deactivating plugins"), deactivation_error);
508 509
		gnumeric_error_info_dialog_show (
			GTK_WINDOW (pm_gui->dialog_pm), error);
510 511 512 513
		error_info_free (error);
	}
}

Chyla Zbigniew's avatar
Chyla Zbigniew committed
514 515 516
static void
pm_dialog_init (PluginManagerGUI *pm_gui)
{
517 518 519
	GSList *sorted_plugin_list;
	GtkTreeIter iter;

520 521 522 523 524 525
	g_signal_connect (G_OBJECT (pm_gui->button_activate_all),
		"clicked",
		G_CALLBACK (cb_pm_button_activate_all_clicked), pm_gui);
	g_signal_connect (G_OBJECT (pm_gui->button_deactivate_all),
		"clicked",
		G_CALLBACK (cb_pm_button_deactivate_all_clicked), pm_gui);
526
	g_signal_connect_swapped (G_OBJECT (pm_gui->button_rescan_directories),
527
		"clicked",
528
		G_CALLBACK (cb_pm_button_rescan_directories_clicked), pm_gui);
529 530 531 532 533 534
	g_signal_connect (G_OBJECT (pm_gui->button_directory_add),
		"clicked",
		G_CALLBACK (cb_pm_button_directory_add_clicked), pm_gui);
	g_signal_connect (G_OBJECT (pm_gui->button_directory_delete),
		"clicked",
		G_CALLBACK (cb_pm_button_directory_delete_clicked), pm_gui);
535 536 537
	g_signal_connect (G_OBJECT (pm_gui->checkbutton_install_new),
		"toggled",
		G_CALLBACK (cb_pm_checkbutton_install_new_toggled), pm_gui);
538 539 540
	g_signal_connect (G_OBJECT (pm_gui->dialog_pm),
		"destroy",
		G_CALLBACK (pm_dialog_cleanup), pm_gui);
Morten Welinder's avatar
Morten Welinder committed
541

Chyla Zbigniew's avatar
Chyla Zbigniew committed
542
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pm_gui->checkbutton_install_new),
543
				      gnm_app_prefs->activate_new_plugins);
Chyla Zbigniew's avatar
Chyla Zbigniew committed
544

545
	/* initialize plugin list */
546
	gtk_list_store_clear (pm_gui->model_plugins);
547 548 549 550 551
	sorted_plugin_list = g_slist_sort (
		g_slist_copy (plugins_get_available_plugins ()),
		&plugin_compare_name);
	GNM_SLIST_FOREACH (sorted_plugin_list, GnmPlugin, plugin,
		gtk_list_store_append (pm_gui->model_plugins, &iter);
552
		set_plugin_model_row (pm_gui, &iter, plugin);
553
	);
554
	g_slist_free (sorted_plugin_list);
555

556
	cb_pm_selection_changed (pm_gui->selection, pm_gui);
Chyla Zbigniew's avatar
Chyla Zbigniew committed
557
}
558

559
static void
Morten Welinder's avatar
Morten Welinder committed
560
cb_pm_dir_selection_changed (G_GNUC_UNUSED GtkTreeSelection *ignored,
561
			     PluginManagerGUI *pm_gui)
562 563 564 565 566 567 568 569 570 571
{
	GtkTreeIter  iter;
	gboolean is_system;

	if (!gtk_tree_selection_get_selected (pm_gui->selection_directory, NULL, &iter)) {
		gtk_widget_set_sensitive (GTK_WIDGET (pm_gui->button_directory_delete), FALSE);
		return;
	}

	gtk_tree_model_get (GTK_TREE_MODEL (pm_gui->model_directories), &iter,
572 573
			    DIR_IS_SYSTEM, &is_system,
			    -1);
574 575 576 577 578

	gtk_widget_set_sensitive (GTK_WIDGET (pm_gui->button_directory_delete), !is_system);
}


579
static void
Morten Welinder's avatar
Morten Welinder committed
580
cb_active_toggled (G_GNUC_UNUSED GtkCellRendererToggle *celltoggle,
581
		   char *path,
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618
                   PluginManagerGUI *pm_gui)
{
	GtkTreeModel *model;
	GtkTreeIter iter;
	GnmPlugin *plugin;
	ErrorInfo *error;

	model = gtk_tree_view_get_model (pm_gui->list_plugins);
	gtk_tree_model_get_iter_from_string (model, &iter, path);
	gtk_tree_model_get (model, &iter, PLUGIN_POINTER, &plugin, -1);
	g_assert (plugin != NULL);
	if (gnm_plugin_is_active (plugin)) {
		gnm_plugin_deactivate (plugin, &error);
	} else {
		GSList *dep_ids;
		int n_inactive_deps = 0;
		gboolean want_activate = TRUE;

		dep_ids = gnm_plugin_get_dependencies_ids (plugin);
		if (dep_ids != NULL) {
			GString *s;

			s = g_string_new (_("The following extra plugins must be activated in order to activate this one:\n\n"));
			GNM_SLIST_FOREACH (dep_ids, char, plugin_id,
				GnmPlugin *plugin;

				plugin = plugins_get_plugin_by_id (plugin_id);
				if (plugin == NULL) {
					g_string_append_printf (s, _("Unknown plugin with id=\"%s\"\n"), plugin_id);
				} else if (!gnm_plugin_is_active (plugin)) {
					g_string_append (s, gnm_plugin_get_name (plugin));
					g_string_append_c (s, '\n');
					n_inactive_deps++;
				}
			);
			g_string_append (s, _("\nDo you want to activate this plugin together with its dependencies?"));
			if (n_inactive_deps > 0) {
619
				want_activate = gnumeric_dialog_question_yes_no (GTK_WINDOW (pm_gui->dialog_pm), s->str, TRUE);
620 621 622 623 624 625 626
			}
			g_string_free (s, TRUE);
		}
		g_slist_free_custom (dep_ids, g_free);

		if (want_activate) {
			gnm_plugin_activate (plugin, &error);
627 628
		} else {
			error = NULL;
629 630 631 632 633
		}
	}
	if (error != NULL) {
		ErrorInfo *new_error;

634
		if (gnm_plugin_is_active (plugin)) {
635 636 637 638 639 640 641 642 643
			new_error = error_info_new_printf (
				_("Error while deactivating plugin \"%s\"."),
				gnm_plugin_get_name (plugin));
		} else {
			new_error = error_info_new_printf (
				_("Error while activating plugin \"%s\"."),
				gnm_plugin_get_name (plugin));
		}
		error_info_add_details (new_error, error);
Jody Goldberg's avatar
Jody Goldberg committed
644
		gnm_cmd_context_error_info (GNM_CMD_CONTEXT (pm_gui->wbcg), new_error);
645 646 647
	}
}

648 649 650 651 652 653 654 655
static void
cb_pm_close_clicked (G_GNUC_UNUSED GtkWidget *button,
			PluginManagerGUI *pm_gui)
{
	gtk_widget_destroy (GTK_WIDGET(pm_gui->dialog_pm));
	return;
}

656
void
657
dialog_plugin_manager (WorkbookControlGUI *wbcg)
658
{
Chyla Zbigniew's avatar
Chyla Zbigniew committed
659
	PluginManagerGUI *pm_gui;
660
	GladeXML *gui;
661
	GtkWidget *scrolled;
662
	GtkWidget *scrolled_directories;
Jody Goldberg's avatar
Jody Goldberg committed
663
	GtkWidget *hbox;
664
	GtkTreeViewColumn *column;
665
	GtkCellRenderer *rend;
666

667
	g_return_if_fail (wbcg != NULL);
668
	g_return_if_fail (IS_WORKBOOK_CONTROL_GUI (wbcg));
669

670 671 672
	if (gnumeric_dialog_raise_if_exists (wbcg, PLUGIN_MANAGER_DIALOG_KEY))
		return;

673
	gui = gnm_glade_xml_new (GNM_CMD_CONTEXT (wbcg),
674 675 676
		"plugin-manager.glade", NULL, NULL);
	if (gui == NULL)
		return;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
677 678 679

	pm_gui = g_new (PluginManagerGUI, 1);
	pm_gui->wbcg = wbcg;
680
	pm_gui->gui = gui;
Jody Goldberg's avatar
Jody Goldberg committed
681
	pm_gui->dialog_pm = GTK_DIALOG (glade_xml_get_widget (gui, "dialog_plugin_manager"));
682 683 684

	/* Set-up plugin list  page */

Morten Welinder's avatar
Morten Welinder committed
685
	pm_gui->button_activate_all =
686 687 688
		GTK_BUTTON (glade_xml_get_widget (gui, "button_activate_all"));
	pm_gui->button_deactivate_all =
		GTK_BUTTON (glade_xml_get_widget (gui, "button_deactivate_all"));
689 690
	pm_gui->button_rescan_directories = GTK_BUTTON (glade_xml_get_widget
						    (gui, "button_rescan_directories"));
691
	pm_gui->checkbutton_install_new = GTK_CHECK_BUTTON (glade_xml_get_widget
692
							    (gui, "checkbutton_install_new"));
693

694 695
	pm_gui->model_plugins = gtk_list_store_new (
		NUM_COLMNS, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_POINTER);
696 697
	pm_gui->list_plugins = GTK_TREE_VIEW (
		gtk_tree_view_new_with_model (GTK_TREE_MODEL (pm_gui->model_plugins)));
698 699
	pm_gui->selection = gtk_tree_view_get_selection (pm_gui->list_plugins);
	gtk_tree_selection_set_mode (pm_gui->selection, GTK_SELECTION_BROWSE);
700 701 702
	g_signal_connect (G_OBJECT (pm_gui->selection),
		"changed",
		G_CALLBACK (cb_pm_selection_changed), pm_gui);
703 704 705 706 707 708 709 710 711

	rend = gtk_cell_renderer_toggle_new ();
	g_signal_connect (G_OBJECT (rend),
		"toggled", G_CALLBACK (cb_active_toggled), pm_gui);
	column = gtk_tree_view_column_new_with_attributes (
		_("Active"), rend,
		"active", PLUGIN_ACTIVE,
		"activatable", PLUGIN_SWITCHABLE,
		NULL);
712
	gtk_tree_view_append_column (pm_gui->list_plugins, column);
713
	column = gtk_tree_view_column_new_with_attributes (_("Plugin name"),
714 715 716 717
							   gtk_cell_renderer_text_new (),
							   "text", PLUGIN_NAME, NULL);
	gtk_tree_view_column_set_sort_column_id (column, PLUGIN_NAME);
	gtk_tree_view_append_column (pm_gui->list_plugins, column);
718
	scrolled = glade_xml_get_widget (gui, "scrolled_plugin_list");
719 720
	gtk_container_add (GTK_CONTAINER (scrolled), GTK_WIDGET (pm_gui->list_plugins));

721 722 723
	/* Set-up plugin details page */

	pm_gui->text_description = gtk_text_view_get_buffer (GTK_TEXT_VIEW (
724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740
				           glade_xml_get_widget (gui, "textview_plugin_description")));
	pm_gui->entry_directory = GTK_ENTRY (glade_xml_get_widget (gui, "entry_directory"));

	pm_gui->model_details = gtk_tree_store_new (
		DETAILS_NUM_COLMNS, G_TYPE_STRING, G_TYPE_STRING);
	pm_gui->view_details = GTK_TREE_VIEW (
		gtk_tree_view_new_with_model (GTK_TREE_MODEL (pm_gui->model_details)));
	column = gtk_tree_view_column_new_with_attributes (
		_("Description"), gtk_cell_renderer_text_new (),
		"text", DETAILS_DESC, NULL);
	gtk_tree_view_append_column (pm_gui->view_details, column);
	column = gtk_tree_view_column_new_with_attributes (
		_("ID"), gtk_cell_renderer_text_new (),
		"text", DETAILS_ID, NULL);
	gtk_tree_view_append_column (pm_gui->view_details, column);
	scrolled = glade_xml_get_widget (gui, "scrolled_plugin_details");
	gtk_container_add (GTK_CONTAINER (scrolled), GTK_WIDGET (pm_gui->view_details));
741

742 743
	pm_gui->frame_mark_for_deactivation =
		glade_xml_get_widget (gui, "frame_mark_for_deactivation");
Morten Welinder's avatar
Morten Welinder committed
744
	pm_gui->checkbutton_mark_for_deactivation =
745
		glade_xml_get_widget (gui, "checkbutton_mark_for_deactivation");
746 747 748

	/* Set-up directories page */

Jody Goldberg's avatar
Jody Goldberg committed
749
	hbox = glade_xml_get_widget (gui, "directory-box");
Morten Welinder's avatar
Morten Welinder committed
750
	pm_gui->model_directories = gtk_list_store_new (DIR_NUM_COLMNS, G_TYPE_STRING,
751 752 753 754 755 756 757
							G_TYPE_BOOLEAN);
	pm_gui->list_directories = GTK_TREE_VIEW (
		gtk_tree_view_new_with_model (GTK_TREE_MODEL (pm_gui->model_directories)));
	pm_gui->selection_directory = gtk_tree_view_get_selection (pm_gui->list_directories);
	gtk_tree_selection_set_mode (pm_gui->selection_directory, GTK_SELECTION_BROWSE);
	column = gtk_tree_view_column_new_with_attributes (_("Directory"),
							   gtk_cell_renderer_text_new (),
Morten Welinder's avatar
Morten Welinder committed
758
							   "text", DIR_NAME,
759 760 761 762
							   NULL);
	gtk_tree_view_column_set_sort_column_id (column, DIR_NAME);
	gtk_tree_view_append_column (pm_gui->list_directories, column);
	scrolled_directories = glade_xml_get_widget (gui, "scrolled_directories");
Morten Welinder's avatar
Morten Welinder committed
763
	gtk_container_add (GTK_CONTAINER (scrolled_directories),
764 765 766 767
			   GTK_WIDGET (pm_gui->list_directories));

	pm_gui->button_directory_add = GTK_BUTTON (glade_xml_get_widget
						  (gui, "button_directory_add"));
768
	gtk_button_set_alignment (GTK_BUTTON (pm_gui->button_directory_add), 0., .5);
769 770
	pm_gui->button_directory_delete = GTK_BUTTON (glade_xml_get_widget
						  (gui, "button_directory_delete"));
771
	gtk_button_set_alignment (GTK_BUTTON (pm_gui->button_directory_delete), 0., .5);
772 773 774 775 776 777 778 779

	cb_pm_dir_selection_changed (NULL, pm_gui);
	g_signal_connect (pm_gui->selection_directory,
		"changed",
		G_CALLBACK (cb_pm_dir_selection_changed), pm_gui);

	/* Done setting up pages */

780
	pm_gui->gnotebook = GTK_NOTEBOOK (glade_xml_get_widget (gui, "notebook1"));
781

Chyla Zbigniew's avatar
Chyla Zbigniew committed
782 783
	gtk_widget_show_all (GTK_WIDGET (pm_gui->gnotebook));

784
	pm_gui_load_directory_page (pm_gui);
785

Chyla Zbigniew's avatar
Chyla Zbigniew committed
786
	pm_dialog_init (pm_gui);
787 788
	gnumeric_init_help_button (
		glade_xml_get_widget (gui, "help_button"),
789
		GNUMERIC_HELP_LINK_PLUGIN_MANAGER);
790 791 792
	g_signal_connect (glade_xml_get_widget (gui, "button_close_manager"),
		"clicked",
		G_CALLBACK (cb_pm_close_clicked), pm_gui);
793

794 795 796
	gnumeric_keyed_dialog (wbcg, GTK_WINDOW (pm_gui->dialog_pm),
			       PLUGIN_MANAGER_DIALOG_KEY);
	gtk_widget_show (GTK_WIDGET (pm_gui->dialog_pm));
797
}