talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

prefs_actions.c (35617B)


      1 /*
      2  * Claws Mail -- a GTK based, lightweight, and fast e-mail client
      3  * Copyright (C) 1999-2024 the Claws Mail Team and Hiroyuki Yamamoto
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation; either version 3 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
     17  */
     18 
     19 #include "defs.h"
     20 
     21 #include <glib.h>
     22 #include <glib/gi18n.h>
     23 #include <gtk/gtk.h>
     24 #include <gdk/gdkkeysyms.h>
     25 #include <stdio.h>
     26 #include <stdlib.h>
     27 #include <string.h>
     28 #include <err.h>
     29 #include <errno.h>
     30 
     31 #include "prefs_gtk.h"
     32 #include "inc.h"
     33 #include "utils.h"
     34 #include "gtkutils.h"
     35 #include "manage_window.h"
     36 #include "mainwindow.h"
     37 #include "prefs_common.h"
     38 #include "alertpanel.h"
     39 #include "prefs_actions.h"
     40 #include "action.h"
     41 #include "manual.h"
     42 #include "menu.h"
     43 #include "prefs_toolbar.h"
     44 #include "file-utils.h"
     45 
     46 enum {
     47 	PREFS_ACTIONS_STRING,	/*!< string pointer managed by list store,
     48 				 *   and never touched or retrieved by
     49 				 *   us */
     50 	PREFS_ACTIONS_DATA,	/*!< pointer to string that is not managed by
     51 				 *   the list store, and which is retrieved
     52 				 *   and touched by us */
     53 	PREFS_ACTIONS_VALID,	/*!< contains a valid action, otherwise "(New)" */
     54 	N_PREFS_ACTIONS_COLUMNS
     55 };
     56 
     57 static struct Actions
     58 {
     59 	GtkWidget *window;
     60 
     61 	GtkWidget *ok_btn;
     62 	GtkWidget *name_entry;
     63 	GtkWidget *cmd_entry;
     64 	GtkWidget *info_btn;
     65 	GtkWidget *shell_radiobtn;
     66 	GtkWidget *filter_radiobtn;
     67 
     68 	GtkWidget *actions_list_view;
     69 } actions;
     70 
     71 static int modified = FALSE;
     72 static int modified_list = FALSE;
     73 
     74 /* widget creating functions */
     75 static void prefs_actions_create	(MainWindow *mainwin);
     76 static void prefs_actions_set_dialog	(void);
     77 static gint prefs_actions_clist_set_row	(gint row);
     78 
     79 static void prefs_actions_register_cb	(GtkWidget	*w,
     80 					 gpointer	 data);
     81 static void prefs_actions_substitute_cb	(GtkWidget	*w,
     82 					 gpointer	 data);
     83 static void prefs_actions_delete_cb	(gpointer gtk_action, gpointer data);
     84 static void prefs_actions_delete_all_cb	(gpointer gtk_action, gpointer data);
     85 static void prefs_actions_clear_cb	(gpointer gtk_action, gpointer data);
     86 static void prefs_actions_duplicate_cb	(gpointer gtk_action, gpointer data);
     87 static void prefs_actions_top_cb		(GtkWidget *w, gpointer data);
     88 static void prefs_actions_up_cb		(GtkWidget *w, gpointer data);
     89 static void prefs_actions_down_cb	(GtkWidget *w, gpointer data);
     90 static void prefs_actions_bottom_cb	(GtkWidget *w, gpointer data);
     91 static gint prefs_actions_deleted	(GtkWidget	*widget,
     92 					 GdkEventAny	*event,
     93 					 gpointer	*data);
     94 static gboolean prefs_actions_key_pressed(GtkWidget	*widget,
     95 					  GdkEventKey	*event,
     96 					  gpointer	 data);
     97 static gboolean prefs_actions_search_func_cb (GtkTreeModel *model, gint column,
     98 						const gchar *key, GtkTreeIter *iter,
     99 						gpointer search_data);
    100 static void prefs_actions_cancel	(GtkWidget	*w,
    101 					 gpointer	 data);
    102 static void prefs_actions_ok		(GtkWidget	*w,
    103 					 gpointer	 data);
    104 
    105 static GtkListStore* prefs_actions_create_data_store	(void);
    106 
    107 static void prefs_actions_list_view_insert_action	(GtkWidget *list_view,
    108 							 gint row,
    109 							 gchar *action,
    110 							 gboolean is_valid);
    111 static GtkWidget *prefs_actions_list_view_create	(void);
    112 static void prefs_actions_create_list_view_columns	(GtkWidget *list_view);
    113 static void prefs_actions_select_row(GtkTreeView *list_view, GtkTreePath *path);
    114 
    115 static void prefs_action_shell_radiobtn_cb(GtkWidget *widget, gpointer data);
    116 
    117 void prefs_actions_open(MainWindow *mainwin)
    118 {
    119 	inc_lock();
    120 
    121 	if (!actions.window)
    122 		prefs_actions_create(mainwin);
    123 
    124 	manage_window_set_transient(GTK_WINDOW(actions.window));
    125 	gtk_widget_grab_focus(actions.ok_btn);
    126 
    127 	prefs_actions_set_dialog();
    128 
    129 	gtk_widget_show(actions.window);
    130 	gtk_window_set_modal(GTK_WINDOW(actions.window), TRUE);
    131 }
    132 
    133 /*!
    134  *\brief	Save Gtk object size to prefs dataset
    135  */
    136 static void prefs_actions_size_allocate_cb(GtkWidget *widget,
    137 					 GtkAllocation *allocation)
    138 {
    139 	cm_return_if_fail(allocation != NULL);
    140 
    141 	gtk_window_get_size(GTK_WINDOW(widget),
    142 		&prefs_common.actionswin_width, &prefs_common.actionswin_height);
    143 }
    144 
    145 static void prefs_actions_create(MainWindow *mainwin)
    146 {
    147 	GtkWidget *window;
    148 	GtkWidget *vbox;
    149 	GtkWidget *filter_hbox;
    150 	GtkWidget *help_btn;
    151 	GtkWidget *ok_btn;
    152 	GtkWidget *cancel_btn;
    153 	GtkWidget *confirm_area;
    154 
    155 	GtkWidget *vbox1;
    156 	GtkWidget *table;
    157 
    158 	GtkWidget *shell_radiobtn;
    159 
    160 	GtkWidget *name_label;
    161 	GtkWidget *name_entry;
    162 	GtkWidget *cmd_label;
    163 	GtkWidget *cmd_entry;
    164 
    165 	GtkWidget *reg_hbox;
    166 	GtkWidget *btn_hbox;
    167 	GtkWidget *arrow;
    168 	GtkWidget *reg_btn;
    169 	GtkWidget *subst_btn;
    170 	GtkWidget *del_btn;
    171 	GtkWidget *clear_btn;
    172 
    173 	GtkWidget *cond_hbox;
    174 	GtkWidget *cond_scrolledwin;
    175 	GtkWidget *cond_list_view;
    176 
    177 	GtkWidget *info_btn;
    178 
    179 	GtkWidget *btn_vbox;
    180 	GtkWidget *spc_vbox;
    181 	GtkWidget *top_btn;
    182 	GtkWidget *up_btn;
    183 	GtkWidget *down_btn;
    184 	GtkWidget *bottom_btn;
    185 	static GdkGeometry geometry;
    186 
    187 	debug_print("Creating actions configuration window...\n");
    188 
    189 	window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "prefs_actions");
    190 
    191 	gtk_container_set_border_width(GTK_CONTAINER (window), 8);
    192 	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
    193 	gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
    194 	gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
    195 
    196 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
    197 	gtk_widget_show(vbox);
    198 	gtk_container_add(GTK_CONTAINER(window), vbox);
    199 
    200 	gtkut_stock_button_set_create_with_help(&confirm_area, &help_btn,
    201 			&cancel_btn, NULL, _("_Cancel"),
    202 			&ok_btn, NULL, _("_OK"),
    203 			NULL, NULL, NULL);
    204 	gtk_widget_show(confirm_area);
    205 	gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
    206 	gtk_widget_grab_default(ok_btn);
    207 
    208 	gtk_window_set_title(GTK_WINDOW(window), _("Actions configuration"));
    209 	g_signal_connect(G_OBJECT(window), "delete_event",
    210 			 G_CALLBACK(prefs_actions_deleted), NULL);
    211 	g_signal_connect(G_OBJECT(window), "size_allocate",
    212 			 G_CALLBACK(prefs_actions_size_allocate_cb), NULL);
    213 	g_signal_connect(G_OBJECT(window), "key_press_event",
    214 			 G_CALLBACK(prefs_actions_key_pressed), NULL);
    215 	MANAGE_WINDOW_SIGNALS_CONNECT(window);
    216 	g_signal_connect(G_OBJECT(ok_btn), "clicked",
    217 			 G_CALLBACK(prefs_actions_ok), mainwin);
    218 	g_signal_connect(G_OBJECT(cancel_btn), "clicked",
    219 			 G_CALLBACK(prefs_actions_cancel), NULL);
    220 	g_signal_connect(G_OBJECT(help_btn), "clicked",
    221 			 G_CALLBACK(manual_open_with_anchor_cb),
    222 			 MANUAL_ANCHOR_ACTIONS);
    223 
    224 	vbox1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, VSPACING);
    225 	gtk_widget_show(vbox1);
    226 	gtk_box_pack_start(GTK_BOX(vbox), vbox1, TRUE, TRUE, 0);
    227 	gtk_container_set_border_width(GTK_CONTAINER(vbox1), 2);
    228 
    229 	table = gtk_grid_new();
    230 	gtk_widget_show(table);
    231 	gtk_grid_set_row_spacing(GTK_GRID(table), VSPACING_NARROW_2);
    232 	gtk_grid_set_column_spacing(GTK_GRID(table), 4);
    233 	gtk_box_pack_start (GTK_BOX (vbox1), table, FALSE, FALSE, 0);
    234 
    235 	name_label = gtk_label_new (_("Menu name"));
    236 	gtk_widget_show (name_label);
    237 	gtk_label_set_xalign (GTK_LABEL (name_label), 1.0);
    238 	gtk_grid_attach(GTK_GRID(table), name_label, 0, 0, 1, 1);
    239 
    240 	name_entry = gtk_entry_new ();
    241 	gtk_widget_show (name_entry);
    242 	gtk_grid_attach(GTK_GRID(table), name_entry, 1, 0, 1, 1);
    243 	gtk_widget_set_hexpand(name_entry, TRUE);
    244 	gtk_widget_set_halign(name_entry, GTK_ALIGN_FILL);
    245 
    246 	cmd_label = gtk_label_new (_("Command"));
    247 	gtk_widget_show (cmd_label);
    248 	gtk_label_set_xalign (GTK_LABEL (cmd_label), 1.0);
    249 	gtk_grid_attach(GTK_GRID(table), cmd_label, 0, 2, 1, 1);
    250 
    251 	cmd_entry = gtk_entry_new ();
    252 	gtk_widget_show (cmd_entry);
    253 	gtk_grid_attach(GTK_GRID(table), cmd_entry, 1, 2, 1, 1);
    254 	gtk_widget_set_hexpand(cmd_entry, TRUE);
    255 	gtk_widget_set_halign(cmd_entry, GTK_ALIGN_FILL);
    256 
    257 	/* radio buttons for filter actions or shell */
    258 	filter_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL,4);
    259 	gtk_grid_attach(GTK_GRID(table), filter_hbox, 1, 3, 1, 1);
    260 	gtk_widget_set_hexpand(filter_hbox, TRUE);
    261 	gtk_widget_set_halign(filter_hbox, GTK_ALIGN_FILL);
    262 	gtk_widget_show(filter_hbox);
    263 
    264 	shell_radiobtn = gtk_radio_button_new_with_label(NULL, _("Shell command"));
    265 	gtk_box_pack_start(GTK_BOX(filter_hbox), shell_radiobtn, FALSE, FALSE, 0);
    266 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(shell_radiobtn), TRUE);
    267 	gtk_widget_show(shell_radiobtn);
    268 
    269 	g_signal_connect(G_OBJECT(shell_radiobtn), "clicked",
    270 			 G_CALLBACK(prefs_action_shell_radiobtn_cb), NULL);
    271 
    272 	/* register / substitute / delete */
    273 
    274 	reg_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
    275 	gtk_widget_show(reg_hbox);
    276 	gtk_box_pack_start(GTK_BOX(vbox1), reg_hbox, FALSE, FALSE, 0);
    277 
    278 	arrow = gtk_image_new_from_icon_name("pan-down-symbolic", GTK_ICON_SIZE_MENU);
    279 	gtk_widget_show(arrow);
    280 	gtk_box_pack_start(GTK_BOX(reg_hbox), arrow, FALSE, FALSE, 0);
    281 	gtk_widget_set_size_request(arrow, -1, 16);
    282 
    283 	btn_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
    284 	gtk_widget_show(btn_hbox);
    285 	gtk_box_pack_start(GTK_BOX(reg_hbox), btn_hbox, FALSE, FALSE, 0);
    286 
    287 	reg_btn = gtkut_stock_button("list-add", _("_Add"));
    288 	gtk_widget_show(reg_btn);
    289 	gtk_box_pack_start(GTK_BOX(btn_hbox), reg_btn, FALSE, TRUE, 0);
    290 	g_signal_connect(G_OBJECT(reg_btn), "clicked",
    291 			 G_CALLBACK(prefs_actions_register_cb), NULL);
    292 	CLAWS_SET_TIP(reg_btn,
    293 			_("Append the new action above to the list"));
    294 
    295 	subst_btn = gtkut_get_replace_btn(_("_Replace"));
    296 	gtk_widget_show(subst_btn);
    297 	gtk_box_pack_start(GTK_BOX(btn_hbox), subst_btn, FALSE, TRUE, 0);
    298 	g_signal_connect(G_OBJECT(subst_btn), "clicked",
    299 			 G_CALLBACK(prefs_actions_substitute_cb), NULL);
    300 	CLAWS_SET_TIP(subst_btn,
    301 			_("Replace the selected action in list with the action above"));
    302 
    303 	del_btn = gtkut_stock_button("list-remove", _("_Remove"));
    304 	gtk_widget_show(del_btn);
    305 	gtk_box_pack_start(GTK_BOX(btn_hbox), del_btn, FALSE, TRUE, 0);
    306 	g_signal_connect(G_OBJECT(del_btn), "clicked",
    307 			 G_CALLBACK(prefs_actions_delete_cb), NULL);
    308 	CLAWS_SET_TIP(del_btn,
    309 			_("Delete the selected action from the list"));
    310 
    311 	clear_btn = gtkut_stock_button("edit-clear", _("C_lear"));
    312 	gtk_widget_show (clear_btn);
    313 	gtk_box_pack_start (GTK_BOX (btn_hbox), clear_btn, FALSE, TRUE, 0);
    314 	g_signal_connect(G_OBJECT (clear_btn), "clicked",
    315 			G_CALLBACK(prefs_actions_clear_cb), NULL);
    316 	CLAWS_SET_TIP(clear_btn,
    317 			_("Clear all the input fields in the dialog"));
    318 
    319 	cond_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
    320 	gtk_widget_show(cond_hbox);
    321 	gtk_box_pack_start(GTK_BOX(vbox1), cond_hbox, TRUE, TRUE, 0);
    322 
    323 	cond_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
    324 	gtk_widget_show(cond_scrolledwin);
    325 	gtk_widget_set_size_request(cond_scrolledwin, -1, 150);
    326 	gtk_box_pack_start(GTK_BOX(cond_hbox), cond_scrolledwin,
    327 			   TRUE, TRUE, 0);
    328 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (cond_scrolledwin),
    329 				       GTK_POLICY_AUTOMATIC,
    330 				       GTK_POLICY_AUTOMATIC);
    331 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(cond_scrolledwin),
    332 					    GTK_SHADOW_ETCHED_IN);
    333 
    334 	cond_list_view = prefs_actions_list_view_create();
    335 	gtk_widget_show(cond_list_view);
    336 	gtk_container_add(GTK_CONTAINER (cond_scrolledwin), cond_list_view);
    337 
    338 	btn_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
    339 	gtk_widget_show(btn_vbox);
    340 	gtk_box_pack_start(GTK_BOX(cond_hbox), btn_vbox, FALSE, FALSE, 0);
    341 
    342 	top_btn = gtkut_stock_button("go-top", _("_Top"));
    343 	gtk_widget_show(top_btn);
    344 	gtk_box_pack_start(GTK_BOX(btn_vbox), top_btn, FALSE, FALSE, 0);
    345 	g_signal_connect(G_OBJECT(top_btn), "clicked",
    346 			 G_CALLBACK(prefs_actions_top_cb), NULL);
    347 	CLAWS_SET_TIP(top_btn,
    348 			_("Move the selected action to the top"));
    349 
    350 	PACK_SPACER(btn_vbox, spc_vbox, VSPACING_NARROW_2);
    351 
    352 	up_btn = gtkut_stock_button("go-up", _("_Up"));
    353 	gtk_widget_show(up_btn);
    354 	gtk_box_pack_start(GTK_BOX(btn_vbox), up_btn, FALSE, FALSE, 0);
    355 	g_signal_connect(G_OBJECT(up_btn), "clicked",
    356 			 G_CALLBACK(prefs_actions_up_cb), NULL);
    357 	CLAWS_SET_TIP(up_btn,
    358 			_("Move the selected action up"));
    359 
    360 	down_btn = gtkut_stock_button("go-down", _("_Down"));
    361 	gtk_widget_show(down_btn);
    362 	gtk_box_pack_start(GTK_BOX(btn_vbox), down_btn, FALSE, FALSE, 0);
    363 	g_signal_connect(G_OBJECT(down_btn), "clicked",
    364 			 G_CALLBACK(prefs_actions_down_cb), NULL);
    365 	CLAWS_SET_TIP(down_btn,
    366 			_("Move selected action down"));
    367 
    368 	PACK_SPACER(btn_vbox, spc_vbox, VSPACING_NARROW_2);
    369 
    370 	bottom_btn = gtkut_stock_button("go-bottom", _("_Bottom"));
    371 	gtk_widget_show(bottom_btn);
    372 	gtk_box_pack_start(GTK_BOX(btn_vbox), bottom_btn, FALSE, FALSE, 0);
    373 	g_signal_connect(G_OBJECT(bottom_btn), "clicked",
    374 			 G_CALLBACK(prefs_actions_bottom_cb), NULL);
    375 	CLAWS_SET_TIP(bottom_btn,
    376 			_("Move the selected action to the bottom"));
    377 
    378 	if (!geometry.min_height) {
    379 		geometry.min_width = 486;
    380 		geometry.min_height = 322;
    381 	}
    382 
    383 	gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL, &geometry,
    384 				      GDK_HINT_MIN_SIZE);
    385 	gtk_window_set_default_size(GTK_WINDOW(window), prefs_common.actionswin_width,
    386 				    prefs_common.actionswin_height);
    387 
    388 	gtk_widget_show(window);
    389 
    390 	actions.window = window;
    391 	actions.ok_btn = ok_btn;
    392 	actions.info_btn = info_btn;
    393 
    394 	actions.name_entry = name_entry;
    395 	actions.cmd_entry  = cmd_entry;
    396 	actions.shell_radiobtn = shell_radiobtn;
    397 
    398 	actions.actions_list_view = cond_list_view;
    399 }
    400 
    401 static void prefs_actions_reset_dialog(void)
    402 {
    403 	gtk_entry_set_text(GTK_ENTRY(actions.name_entry), "");
    404 	gtk_entry_set_text(GTK_ENTRY(actions.cmd_entry), "");
    405 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(actions.shell_radiobtn), TRUE);
    406 }
    407 
    408 void prefs_actions_read_config(void)
    409 {
    410 	FILE *fp;
    411 	gchar buf[PREFSBUFSIZE];
    412 	gchar *act;
    413 
    414 	char rcpath[PATH_MAX];
    415 	strlcpy(rcpath, get_rc_dir(), sizeof(rcpath));
    416 	strlcat(rcpath, "/actionsrc", sizeof(rcpath));
    417 	if ((fp = fopen(rcpath, "r")) == NULL) {
    418 		warn("open %s", rcpath);
    419 		return;
    420 	}
    421 
    422 	while (prefs_common.actions_list != NULL) {
    423 		act = (gchar *)prefs_common.actions_list->data;
    424 		prefs_common.actions_list =
    425 			g_slist_remove(prefs_common.actions_list, act);
    426 		g_free(act);
    427 	}
    428 
    429 	while (fgets(buf, sizeof(buf), fp) != NULL) {
    430 		const gchar *src_codeset = conv_get_locale_charset_str();
    431 		const gchar *dest_codeset = CS_UTF_8;
    432 		gchar *tmp;
    433 
    434 		tmp = conv_codeset_strdup(buf, src_codeset, dest_codeset);
    435 		if (!tmp) {
    436 			g_warning("failed to convert character set of action configuration");
    437 			tmp = g_strdup(buf);
    438 		}
    439 
    440 		g_strchomp(tmp);
    441 		act = strstr(tmp, ": ");
    442 		if (act && act[2] &&
    443 		    action_get_type(&act[2]) != ACTION_ERROR)
    444 			prefs_common.actions_list =
    445 				g_slist_append(prefs_common.actions_list,
    446 					       tmp);
    447 		else
    448 			g_free(tmp);
    449 	}
    450 	fclose(fp);
    451 }
    452 
    453 void prefs_actions_write_config(void)
    454 {
    455 	PrefFile *pfile;
    456 	GSList *cur;
    457 
    458 	debug_print("Writing actions configuration...\n");
    459 
    460 	char rcpath[PATH_MAX];
    461 	strlcpy(rcpath, get_rc_dir(), sizeof(rcpath));
    462 	strlcat(rcpath, "/actionsrc", sizeof(rcpath));
    463 	if ((pfile= prefs_write_open(rcpath)) == NULL) {
    464 		warn("write actions configuration file %s", rcpath);
    465 		return;
    466 	}
    467 
    468 	for (cur = prefs_common.actions_list; cur != NULL; cur = cur->next) {
    469 		gchar *tmp = (gchar *)cur->data;
    470 		const gchar *src_codeset = CS_UTF_8;
    471 		const gchar *dest_codeset = conv_get_locale_charset_str();
    472 		gchar *act;
    473 
    474 		act = conv_codeset_strdup(tmp, src_codeset, dest_codeset);
    475 		if (!act) {
    476 			g_warning("failed to convert character set of action configuration");
    477 			act = g_strdup(act);
    478 		}
    479 
    480 		if (fputs(act, pfile->fp) == EOF ||
    481 		    fputc('\n', pfile->fp) == EOF) {
    482 			FILE_OP_ERROR(rcpath, "fputs || fputc");
    483 			prefs_file_close_revert(pfile);
    484 			g_free(act);
    485 			return;
    486 		}
    487 		g_free(act);
    488 	}
    489 	prefs_file_close(pfile);
    490 }
    491 
    492 static void prefs_actions_clear_list(GtkListStore *list_store)
    493 {
    494 	gtk_list_store_clear(list_store);
    495 
    496 	prefs_actions_list_view_insert_action(actions.actions_list_view,
    497 					      -1, _("(New)"), FALSE);
    498 }
    499 
    500 static void prefs_actions_set_dialog(void)
    501 {
    502 	GtkListStore *store;
    503 	GSList *cur;
    504 
    505 	store = GTK_LIST_STORE(gtk_tree_view_get_model
    506 				(GTK_TREE_VIEW(actions.actions_list_view)));
    507 
    508 	prefs_actions_clear_list(store);
    509 	prefs_actions_reset_dialog();
    510 
    511 	for (cur = prefs_common.actions_list; cur != NULL; cur = cur->next) {
    512 		gchar *action = (gchar *) cur->data;
    513 
    514 		prefs_actions_list_view_insert_action(actions.actions_list_view,
    515 						      -1, action, TRUE);
    516 	}
    517 }
    518 
    519 static void prefs_actions_set_list(void)
    520 {
    521 	GtkTreeIter iter;
    522 	GtkListStore *store;
    523 
    524 	g_slist_free(prefs_common.actions_list);
    525 	prefs_common.actions_list = NULL;
    526 
    527 	store = GTK_LIST_STORE(gtk_tree_view_get_model
    528 				(GTK_TREE_VIEW(actions.actions_list_view)));
    529 
    530 	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) {
    531 		do {
    532 			gchar *action;
    533 			gboolean is_valid;
    534 
    535 			gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
    536 					   PREFS_ACTIONS_DATA, &action,
    537 					   PREFS_ACTIONS_VALID, &is_valid,
    538 					   -1);
    539 
    540 			if (is_valid)
    541 				prefs_common.actions_list =
    542 					g_slist_append(prefs_common.actions_list,
    543 						       action);
    544 
    545 		} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store),
    546 						  &iter));
    547 	}
    548 }
    549 
    550 #define GET_ENTRY(entry) \
    551 	entry_text = gtk_entry_get_text(GTK_ENTRY(entry))
    552 
    553 static gint prefs_actions_clist_set_row(gint row)
    554 {
    555 	const gchar *entry_text;
    556 	gint len;
    557 	gchar action[PREFSBUFSIZE];
    558 	gchar *new_action;
    559 
    560 	GET_ENTRY(actions.name_entry);
    561 	if (entry_text[0] == '\0') {
    562 		alertpanel_error(_("Menu name is not set."));
    563 		return -1;
    564 	}
    565 
    566 	if (entry_text[0] == '/') {
    567 		alertpanel_error(_("A leading '/' is not allowed in the menu name."));
    568 		return -1;
    569 	}
    570 
    571 	if (strchr(entry_text, ':')) {
    572 		alertpanel_error(_("Colon ':' is not allowed in the menu name."));
    573 		return -1;
    574 	}
    575 
    576 	strncpy(action, entry_text, PREFSBUFSIZE - 1);
    577 
    578 	while (strstr(action, "//")) {
    579 		char *to_move = strstr(action, "//")+1;
    580 		char *where = strstr(action, "//");
    581 		int old_len = strlen(action);
    582 		memmove(where, to_move, strlen(to_move));
    583 		action[old_len-1] = '\0';
    584 	}
    585 
    586 	g_strstrip(action);
    587 
    588 	/* Keep space for the ': ' delimiter */
    589 	len = strlen(action) + 2;
    590 	if (len >= PREFSBUFSIZE - 1) {
    591 		alertpanel_error(_("Menu name is too long."));
    592 		return -1;
    593 	}
    594 
    595 	strcat(action, ": ");
    596 
    597 	GET_ENTRY(actions.cmd_entry);
    598 
    599 	if (entry_text[0] == '\0') {
    600 		alertpanel_error(_("Command-line not set."));
    601 		return -1;
    602 	}
    603 
    604 	if (len + strlen(entry_text) >= PREFSBUFSIZE - 1) {
    605 		alertpanel_error(_("Menu name and command are too long."));
    606 		return -1;
    607 	}
    608 
    609 	if (action_get_type(entry_text) == ACTION_ERROR) {
    610 		gchar *message;
    611 		message = g_markup_printf_escaped(_("The command\n%s\nhas a syntax error."),
    612 						entry_text);
    613 		alertpanel_error("%s", message);
    614 		g_free(message);
    615 		return -1;
    616 	}
    617 
    618 	strcat(action, entry_text);
    619 
    620 	new_action = g_strdup(action);
    621 	prefs_actions_list_view_insert_action(actions.actions_list_view,
    622 	                                      row, new_action, TRUE);
    623 
    624 	prefs_actions_set_list();
    625 
    626 	return 0;
    627 }
    628 
    629 /* callback functions */
    630 
    631 static void prefs_actions_register_cb(GtkWidget *w, gpointer data)
    632 {
    633 	prefs_actions_clist_set_row(-1);
    634 
    635 	modified = FALSE;
    636 	modified_list = TRUE;
    637 }
    638 
    639 static void prefs_actions_substitute_cb(GtkWidget *w, gpointer data)
    640 {
    641 	gint row;
    642 
    643 	row = gtkut_list_view_get_selected_row(actions.actions_list_view);
    644 	if (row <= 0)
    645 		return;
    646 
    647 	prefs_actions_clist_set_row(row);
    648 
    649 	modified = FALSE;
    650 	modified_list = TRUE;
    651 }
    652 
    653 static void prefs_actions_delete_cb(gpointer gtk_action, gpointer data)
    654 {
    655 	GtkTreeIter sel;
    656 	GtkTreeModel *model;
    657 	gchar *action;
    658 	gint row;
    659 
    660 	row = gtkut_list_view_get_selected_row(actions.actions_list_view);
    661 	if (row <= 0)
    662 		return;
    663 
    664 	if (!gtk_tree_selection_get_selected(gtk_tree_view_get_selection
    665 				(GTK_TREE_VIEW(actions.actions_list_view)),
    666 				&model, &sel))
    667 		return;
    668 
    669 	if (alertpanel(_("Delete action"),
    670 		       _("Do you really want to delete this action?"),
    671 		       NULL, _("_Cancel"), "edit-delete", _("D_elete"), NULL, NULL,
    672 		       ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
    673 		return;
    674 
    675 	/* XXX: Here's the reason why we need to store the original
    676 	 * pointer: we search the slist for it. */
    677 	gtk_tree_model_get(model, &sel,
    678 			   PREFS_ACTIONS_DATA, &action,
    679 			   -1);
    680 	gtk_list_store_remove(GTK_LIST_STORE(model), &sel);
    681 
    682 	prefs_common.actions_list = g_slist_remove(prefs_common.actions_list,
    683 						   action);
    684 	modified_list = TRUE;
    685 }
    686 
    687 static void prefs_actions_delete_all_cb(gpointer gtk_action, gpointer data)
    688 {
    689 	GtkListStore *list_store;
    690 
    691 	if (alertpanel(_("Delete all actions"),
    692 			  _("Do you really want to delete all the actions?"),
    693 			  NULL, _("_Cancel"), "edit-delete", _("D_elete"), NULL, NULL,
    694 		          ALERTFOCUS_FIRST) != G_ALERTDEFAULT)
    695 	   return;
    696 
    697 	list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(actions.actions_list_view)));
    698 	prefs_actions_clear_list(list_store);
    699 	modified = FALSE;
    700 
    701 	prefs_actions_reset_dialog();
    702 	modified_list = TRUE;
    703 }
    704 
    705 static void prefs_actions_clear_cb(gpointer gtk_action, gpointer data)
    706 {
    707 	gint row;
    708 
    709 	prefs_actions_reset_dialog();
    710 	row = gtkut_list_view_get_selected_row(actions.actions_list_view);
    711 	if (row < 1)
    712 		modified = FALSE;
    713 	else
    714 		modified = TRUE;
    715 }
    716 
    717 static void prefs_actions_duplicate_cb(gpointer gtk_action, gpointer data)
    718 {
    719 	gint row;
    720 
    721 	row = gtkut_list_view_get_selected_row(actions.actions_list_view);
    722 	if (row <= 0)
    723 		return;
    724 
    725 	modified_list = !prefs_actions_clist_set_row(-row-2);
    726 }
    727 
    728 static void prefs_actions_top_cb(GtkWidget *w, gpointer data)
    729 {
    730 	gint row;
    731 	GtkTreeIter top, sel;
    732 	GtkTreeModel *model;
    733 
    734 	row = gtkut_list_view_get_selected_row(actions.actions_list_view);
    735 	if (row <= 1)
    736 		return;
    737 
    738 	model = gtk_tree_view_get_model(GTK_TREE_VIEW(actions.actions_list_view));
    739 
    740 	if (!gtk_tree_model_iter_nth_child(model, &top, NULL, 0)
    741 	||  !gtk_tree_model_iter_nth_child(model, &sel, NULL, row))
    742 		return;
    743 
    744 	gtk_list_store_move_after(GTK_LIST_STORE(model), &sel, &top);
    745 	gtkut_list_view_select_row(actions.actions_list_view, 1);
    746 	modified_list = TRUE;
    747 }
    748 
    749 static void prefs_actions_up_cb(GtkWidget *w, gpointer data)
    750 {
    751 	gint row;
    752 	GtkTreeIter top, sel;
    753 	GtkTreeModel *model;
    754 
    755 	row = gtkut_list_view_get_selected_row(actions.actions_list_view);
    756 	if (row <= 1)
    757 		return;
    758 
    759 	model = gtk_tree_view_get_model(GTK_TREE_VIEW(actions.actions_list_view));
    760 
    761 	if (!gtk_tree_model_iter_nth_child(model, &top, NULL, row - 1)
    762 	||  !gtk_tree_model_iter_nth_child(model, &sel, NULL, row))
    763 		return;
    764 
    765 	gtk_list_store_swap(GTK_LIST_STORE(model), &top, &sel);
    766 	gtkut_list_view_select_row(actions.actions_list_view, row - 1);
    767 	modified_list = TRUE;
    768 }
    769 
    770 static void prefs_actions_down_cb(GtkWidget *w, gpointer data)
    771 {
    772 	gint row, n_rows;
    773 	GtkTreeIter top, sel;
    774 	GtkTreeModel *model;
    775 
    776 	model = gtk_tree_view_get_model(GTK_TREE_VIEW(actions.actions_list_view));
    777 	n_rows = gtk_tree_model_iter_n_children(model, NULL);
    778 	row = gtkut_list_view_get_selected_row(actions.actions_list_view);
    779 	if (row < 1 || row >= n_rows - 1)
    780 		return;
    781 
    782 	if (!gtk_tree_model_iter_nth_child(model, &top, NULL, row)
    783 	||  !gtk_tree_model_iter_nth_child(model, &sel, NULL, row + 1))
    784 		return;
    785 
    786 	gtk_list_store_swap(GTK_LIST_STORE(model), &top, &sel);
    787 	gtkut_list_view_select_row(actions.actions_list_view, row + 1);
    788 	modified_list = TRUE;
    789 }
    790 
    791 static void prefs_actions_bottom_cb(GtkWidget *w, gpointer data)
    792 {
    793 	gint row, n_rows;
    794 	GtkTreeIter top, sel;
    795 	GtkTreeModel *model;
    796 
    797 	model = gtk_tree_view_get_model(GTK_TREE_VIEW(actions.actions_list_view));
    798 	n_rows = gtk_tree_model_iter_n_children(model, NULL);
    799 	row = gtkut_list_view_get_selected_row(actions.actions_list_view);
    800 	if (row < 1 || row >= n_rows - 1)
    801 		return;
    802 
    803 	if (!gtk_tree_model_iter_nth_child(model, &top, NULL, row)
    804 	||  !gtk_tree_model_iter_nth_child(model, &sel, NULL, n_rows - 1))
    805 		return;
    806 
    807 	gtk_list_store_move_after(GTK_LIST_STORE(model), &top, &sel);
    808 	gtkut_list_view_select_row(actions.actions_list_view, n_rows - 1);
    809 	modified_list = TRUE;
    810 }
    811 
    812 static gint prefs_actions_deleted(GtkWidget *widget, GdkEventAny *event,
    813 				  gpointer *data)
    814 {
    815 	prefs_actions_cancel(widget, data);
    816 	return TRUE;
    817 }
    818 
    819 static gboolean prefs_actions_key_pressed(GtkWidget *widget, GdkEventKey *event,
    820 					  gpointer data)
    821 {
    822 	GtkWidget *focused = gtkut_get_focused_child(GTK_CONTAINER(widget));
    823 	if (focused && GTK_IS_EDITABLE(focused))
    824 		modified = TRUE;
    825 	return FALSE;
    826 }
    827 
    828 static gboolean prefs_actions_search_func_cb (GtkTreeModel *model, gint column, const gchar *key,
    829 						GtkTreeIter *iter, gpointer search_data)
    830 {
    831 	gchar *store_string;
    832 	gboolean retval;
    833 	GtkTreePath *path;
    834 
    835 	gtk_tree_model_get (model, iter, column, &store_string, -1);
    836 
    837 	if (!store_string || !key)
    838 		return FALSE;
    839 
    840 
    841 	retval = (strncmp (key, store_string, strlen(key)) != 0);
    842 
    843 	g_free(store_string);
    844 	debug_print("selecting row\n");
    845 	path = gtk_tree_model_get_path(model, iter);
    846 	prefs_actions_select_row(GTK_TREE_VIEW(actions.actions_list_view), path);
    847 	gtk_tree_path_free(path);
    848 
    849 	return retval;
    850 }
    851 static void prefs_actions_cancel(GtkWidget *w, gpointer data)
    852 {
    853 	GtkListStore *store;
    854 
    855 	if (modified && alertpanel(_("Entry not saved"),
    856 				 _("The entry was not saved. Close anyway?"),
    857 				 "window-close", _("_Close"), NULL, _("_Continue editing"),
    858 				 NULL, NULL, ALERTFOCUS_SECOND) != G_ALERTDEFAULT) {
    859 		return;
    860 	} else if (modified_list && alertpanel(_("Actions list not saved"),
    861 				 _("The actions list has been modified. Close anyway?"),
    862 				 "window-close", _("_Close"), NULL, _("_Continue editing"),
    863 				 NULL, NULL, ALERTFOCUS_SECOND) != G_ALERTDEFAULT) {
    864 		return;
    865 	}
    866 	modified = FALSE;
    867 	modified_list = FALSE;
    868 	store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW
    869 				(actions.actions_list_view)));
    870 	gtk_list_store_clear(store);
    871 	prefs_actions_read_config();
    872 	gtk_widget_hide(actions.window);
    873 	gtk_window_set_modal(GTK_WINDOW(actions.window), FALSE);
    874 	inc_unlock();
    875 }
    876 
    877 static void prefs_actions_ok(GtkWidget *widget, gpointer data)
    878 {
    879 	MainWindow *mainwin = (MainWindow *) data;
    880 	const GList *list;
    881 	const GList *iter;
    882 	MessageView *msgview;
    883 	Compose *compose;
    884 	GtkListStore *store;
    885 
    886 	if (modified && alertpanel(_("Entry not saved"),
    887 				 _("The entry was not saved. Close anyway?"),
    888 				 "window-close", _("_Close"), NULL, _("_Continue editing"),
    889 				 NULL, NULL, ALERTFOCUS_SECOND) != G_ALERTDEFAULT) {
    890 		return;
    891 	}
    892 	modified = FALSE;
    893 	modified_list = FALSE;
    894 	prefs_actions_set_list();
    895 	store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW
    896 				(actions.actions_list_view)));
    897 	gtk_list_store_clear(store);
    898 	prefs_actions_write_config();
    899 
    900 	/* Update mainwindow actions menu */
    901 	main_window_update_actions_menu(mainwin);
    902 
    903 	/* Update separated message view actions menu */
    904 	list = messageview_get_msgview_list();
    905 	for (iter = list; iter; iter = iter->next) {
    906 		msgview = (MessageView *) iter->data;
    907 		messageview_update_actions_menu(msgview);
    908 	}
    909 
    910 	/* Update compose windows actions menu */
    911 	list = compose_get_compose_list();
    912 	for (iter = list; iter; iter = iter->next) {
    913 		compose = (Compose *) iter->data;
    914 		compose_update_actions_menu(compose);
    915 	}
    916 
    917 	/* Update toolbars */
    918 	prefs_toolbar_update_action_btns();
    919 
    920 	gtk_widget_hide(actions.window);
    921 	gtk_window_set_modal(GTK_WINDOW(actions.window), FALSE);
    922 	inc_unlock();
    923 }
    924 
    925 static GtkListStore* prefs_actions_create_data_store(void)
    926 {
    927 	return gtk_list_store_new(N_PREFS_ACTIONS_COLUMNS,
    928 				  G_TYPE_STRING,
    929 				  G_TYPE_POINTER,
    930 				  G_TYPE_BOOLEAN,
    931 				  -1);
    932 }
    933 
    934 static void prefs_actions_list_view_insert_action(GtkWidget *list_view,
    935 						  gint row,
    936 						  gchar *action,
    937 						  gboolean is_valid)
    938 {
    939 	GtkTreeIter iter;
    940 	GtkTreeIter sibling;
    941 	GtkListStore *list_store = GTK_LIST_STORE(gtk_tree_view_get_model
    942 					(GTK_TREE_VIEW(list_view)));
    943 
    944 /*	row -1 to add a new rule to store,
    945 	row >=0 to change an existing row
    946 	row <-1 insert a new row after (-row-2)
    947 */
    948 	if (row >= 0 ) {
    949 		/* modify the existing */
    950  		if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store),
    951  						   &iter, NULL, row))
    952 			row = -1;
    953 	} else if (row < -1 ) {
    954 		if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store),
    955 						   &sibling, NULL, -row-2))
    956  			row = -1;
    957 	}
    958 
    959 	if (row == -1 ) {
    960 		/* append new */
    961 		gtk_list_store_append(list_store, &iter);
    962 		gtk_list_store_set(list_store, &iter,
    963 				   PREFS_ACTIONS_STRING, action,
    964 				   PREFS_ACTIONS_DATA, action,
    965 				   PREFS_ACTIONS_VALID,  is_valid,
    966 				   -1);
    967 	} else if (row < -1) {
    968 		/* duplicate */
    969 		gtk_list_store_insert_after(list_store, &iter, &sibling);
    970 		gtk_list_store_set(list_store, &iter,
    971 				   PREFS_ACTIONS_STRING, action,
    972 				   PREFS_ACTIONS_DATA, action,
    973 				   PREFS_ACTIONS_VALID,  is_valid,
    974 				   -1);
    975 	} else {
    976 		/* change existing */
    977 		gchar *old_action;
    978 
    979 		gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter,
    980 				   PREFS_ACTIONS_DATA, &old_action,
    981 				   -1);
    982 		g_free(old_action);
    983 
    984 		gtk_list_store_set(list_store, &iter,
    985 				   PREFS_ACTIONS_STRING, action,
    986 				   PREFS_ACTIONS_DATA, action,
    987 				   -1);
    988 	}
    989 }
    990 
    991 static GtkActionGroup *prefs_actions_popup_action = NULL;
    992 static GtkWidget *prefs_actions_popup_menu = NULL;
    993 
    994 static GtkActionEntry prefs_actions_popup_entries[] =
    995 {
    996  	{"PrefsActionsPopup",			NULL, "PrefsActionsPopup", NULL, NULL, NULL },
    997 	{"PrefsActionsPopup/Delete",	NULL, N_("_Delete"), NULL, NULL, G_CALLBACK(prefs_actions_delete_cb) },
    998 	{"PrefsActionsPopup/DeleteAll",	NULL, N_("Delete _all"), NULL, NULL, G_CALLBACK(prefs_actions_delete_all_cb) },
    999 	{"PrefsActionsPopup/Duplicate",	NULL, N_("D_uplicate"), NULL, NULL, G_CALLBACK(prefs_actions_duplicate_cb) },
   1000 };
   1001 
   1002 static void prefs_actions_row_selected(GtkTreeSelection *selection, GtkTreeView *list_view)
   1003 {
   1004 	GtkTreePath *path;
   1005 	GtkTreeIter iter;
   1006 	GtkTreeModel *model;
   1007 
   1008 	if (!gtk_tree_selection_get_selected(selection, &model, &iter))
   1009 		return;
   1010 
   1011 	path = gtk_tree_model_get_path(model, &iter);
   1012 	prefs_actions_select_row(list_view, path);
   1013 	gtk_tree_path_free(path);
   1014 }
   1015 
   1016 static gint prefs_actions_list_btn_pressed(GtkWidget *widget, GdkEventButton *event,
   1017 				   GtkTreeView *list_view)
   1018 {
   1019 	if (event) {
   1020 		/* left- or right-button click */
   1021 		if (event->button == 1 || event->button == 3) {
   1022 			GtkTreePath *path = NULL;
   1023 			if (gtk_tree_view_get_path_at_pos( list_view, event->x, event->y,
   1024 					&path, NULL, NULL, NULL)) {
   1025 				prefs_actions_select_row(list_view, path);
   1026 		}
   1027 		if (path)
   1028 			gtk_tree_path_free(path);
   1029 		}
   1030 
   1031 		/* right-button click */
   1032 		if (event->button == 3) {
   1033 			GtkTreeModel *model = gtk_tree_view_get_model(list_view);
   1034 			GtkTreeIter iter;
   1035 			gboolean non_empty;
   1036 			gint row;
   1037 
   1038 			if (!prefs_actions_popup_menu) {
   1039 				prefs_actions_popup_action = cm_menu_create_action_group("PrefsActionsPopup",
   1040 						prefs_actions_popup_entries, G_N_ELEMENTS(prefs_actions_popup_entries),
   1041 						(gpointer)list_view);
   1042 				MENUITEM_ADDUI("/Menus", "PrefsActionsPopup", "PrefsActionsPopup", GTK_UI_MANAGER_MENU)
   1043 				MENUITEM_ADDUI("/Menus/PrefsActionsPopup", "Delete", "PrefsActionsPopup/Delete", GTK_UI_MANAGER_MENUITEM)
   1044 				MENUITEM_ADDUI("/Menus/PrefsActionsPopup", "DeleteAll", "PrefsActionsPopup/DeleteAll", GTK_UI_MANAGER_MENUITEM)
   1045 				MENUITEM_ADDUI("/Menus/PrefsActionsPopup", "Duplicate", "PrefsActionsPopup/Duplicate", GTK_UI_MANAGER_MENUITEM)
   1046 				prefs_actions_popup_menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
   1047 						gtk_ui_manager_get_widget(gtkut_ui_manager(), "/Menus/PrefsActionsPopup")) );
   1048 			}
   1049 
   1050 			/* grey out some popup menu items if there is no selected row */
   1051 			row = gtkut_list_view_get_selected_row(GTK_WIDGET(list_view));
   1052 			cm_menu_set_sensitive("PrefsActionsPopup/Delete", (row > 0));
   1053 			cm_menu_set_sensitive("PrefsActionsPopup/Duplicate", (row > 0));
   1054 
   1055 			/* grey out seom popup menu items if there is no row
   1056 			(not counting the (New) one at row 0) */
   1057 			non_empty = gtk_tree_model_get_iter_first(model, &iter);
   1058 			if (non_empty)
   1059 				non_empty = gtk_tree_model_iter_next(model, &iter);
   1060 			cm_menu_set_sensitive("PrefsActionsPopup/DeleteAll", non_empty);
   1061 
   1062 			gtk_menu_popup_at_pointer(GTK_MENU(prefs_actions_popup_menu), NULL);
   1063 		}
   1064    }
   1065    return FALSE;
   1066 }
   1067 
   1068 static gboolean prefs_actions_list_popup_menu(GtkWidget *widget, gpointer data)
   1069 {
   1070    GtkTreeView *list_view = (GtkTreeView *)data;
   1071    GdkEventButton event;
   1072 
   1073    event.button = 3;
   1074    event.time = gtk_get_current_event_time();
   1075 
   1076    prefs_actions_list_btn_pressed(NULL, &event, list_view);
   1077 
   1078    return TRUE;
   1079 }
   1080 
   1081 static GtkWidget *prefs_actions_list_view_create(void)
   1082 {
   1083 	GtkTreeView *list_view;
   1084 	GtkTreeSelection *selector;
   1085 	GtkTreeModel *model;
   1086 
   1087 	model = GTK_TREE_MODEL(prefs_actions_create_data_store());
   1088 	list_view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(model));
   1089 	g_object_unref(model);
   1090 
   1091 	g_signal_connect(G_OBJECT(list_view), "popup-menu",
   1092 			 G_CALLBACK(prefs_actions_list_popup_menu), list_view);
   1093 	g_signal_connect(G_OBJECT(list_view), "button-press-event",
   1094 			G_CALLBACK(prefs_actions_list_btn_pressed), list_view);
   1095 
   1096 	gtk_tree_view_set_reorderable(list_view, TRUE);
   1097 
   1098 	selector = gtk_tree_view_get_selection(list_view);
   1099 	gtk_tree_selection_set_mode(selector, GTK_SELECTION_BROWSE);
   1100 	g_signal_connect(G_OBJECT(selector), "changed",
   1101 			 G_CALLBACK(prefs_actions_row_selected), list_view);
   1102 
   1103 	/* create the columns */
   1104 	prefs_actions_create_list_view_columns(GTK_WIDGET(list_view));
   1105 
   1106 	return GTK_WIDGET(list_view);
   1107 }
   1108 
   1109 static void prefs_actions_create_list_view_columns(GtkWidget *list_view)
   1110 {
   1111 	GtkTreeViewColumn *column;
   1112 	GtkCellRenderer *renderer;
   1113 
   1114 	renderer = gtk_cell_renderer_text_new();
   1115 	column = gtk_tree_view_column_new_with_attributes
   1116 		(_("Current actions"),
   1117 		 renderer,
   1118 		 "text", PREFS_ACTIONS_STRING,
   1119 		 NULL);
   1120 	gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);
   1121 	gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(list_view), prefs_actions_search_func_cb , NULL, NULL);
   1122 }
   1123 
   1124 #define ENTRY_SET_TEXT(entry, str) \
   1125 	gtk_entry_set_text(GTK_ENTRY(entry), str ? str : "")
   1126 
   1127 static void prefs_actions_select_row(GtkTreeView *list_view, GtkTreePath *path)
   1128 {
   1129 	GtkTreeModel *model = gtk_tree_view_get_model(list_view);
   1130 	GtkTreeSelection *selection;
   1131 	gchar *action;
   1132 	gchar *cmd;
   1133 	gchar buf[PREFSBUFSIZE];
   1134 	GtkTreeIter iter;
   1135 	gboolean is_valid;
   1136 
   1137 	if (!model || !path || !gtk_tree_model_get_iter(model, &iter, path))
   1138 		return;
   1139 
   1140 	/* select row */
   1141 	selection = gtk_tree_view_get_selection(list_view);
   1142 	gtk_tree_selection_select_path(selection, path);
   1143 
   1144 	gtk_tree_model_get(model, &iter,
   1145 			   PREFS_ACTIONS_VALID,  &is_valid,
   1146 			   PREFS_ACTIONS_DATA, &action,
   1147 			   -1);
   1148 	if (!is_valid) {
   1149 		prefs_actions_reset_dialog();
   1150 		return;
   1151 	}
   1152 
   1153 	strncpy(buf, action, PREFSBUFSIZE - 1);
   1154 	buf[PREFSBUFSIZE - 1] = '\0';
   1155 	cmd = strstr(buf, ": ");
   1156 
   1157 	if (cmd && cmd[2])
   1158 		ENTRY_SET_TEXT(actions.cmd_entry, &cmd[2]);
   1159 	else
   1160 		return;
   1161 
   1162 	*cmd = '\0';
   1163 	gtk_entry_set_text(GTK_ENTRY(actions.name_entry), buf);
   1164 
   1165 	if (g_str_has_prefix(&cmd[2], "%as{") == TRUE)
   1166 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
   1167 						actions.filter_radiobtn), TRUE);
   1168 	else
   1169 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
   1170 						actions.shell_radiobtn), TRUE);
   1171 
   1172 	return;
   1173 }
   1174 
   1175 static void prefs_action_shell_radiobtn_cb(GtkWidget *widget, gpointer data)
   1176 {
   1177 	if (actions.cmd_entry)
   1178 		gtk_widget_set_sensitive(actions.cmd_entry, TRUE);
   1179 	if (actions.info_btn)
   1180 		gtk_widget_set_sensitive(actions.info_btn, TRUE);
   1181 }
   1182 
   1183 void prefs_actions_rename_path(const gchar *old_path, const gchar *new_path)
   1184 { }
   1185 
   1186 gint prefs_actions_find_by_name(const gchar *name)
   1187 {
   1188 	GSList *act = prefs_common.actions_list;
   1189 	gchar *action_name, *action_p;
   1190 	gint action_nb = 0;
   1191 
   1192 	for (; act != NULL; act = act->next) {
   1193 		action_name = g_strdup((gchar *)act->data);
   1194 		action_p = strstr(action_name, ": ");
   1195 		action_p[0] = 0x00;
   1196 
   1197 		if (g_utf8_collate(name, action_name) == 0) {
   1198 			g_free(action_name);
   1199 			return action_nb;
   1200 		}
   1201 
   1202 		g_free(action_name);
   1203 		action_nb++;
   1204 	}
   1205 
   1206 	return -1;
   1207 }