talons

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

prefs_customheader.c (20901B)


      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 <errno.h>
     29 
     30 #include "main.h"
     31 #include "prefs_gtk.h"
     32 #include "prefs_customheader.h"
     33 #include "prefs_account.h"
     34 #include "mainwindow.h"
     35 #include "foldersel.h"
     36 #include "manage_window.h"
     37 #include "customheader.h"
     38 #include "folder.h"
     39 #include "utils.h"
     40 #include "gtkutils.h"
     41 #include "alertpanel.h"
     42 #include "filesel.h"
     43 #include "combobox.h"
     44 #include "file-utils.h"
     45 
     46 #define CUSTOM_HEADER_RC "customheaderrc"
     47 
     48 enum {
     49 	CUSTHDR_STRING,		/*!< display string managed by list store */
     50 	CUSTHDR_DATA,		/*!< string managed by us */
     51 	N_CUSTHDR_COLUMNS
     52 };
     53 
     54 static struct CustomHdr {
     55 	GtkWidget *window;
     56 
     57 	GtkWidget *ok_btn;
     58 	GtkWidget *cancel_btn;
     59 
     60 	GtkWidget *hdr_combo;
     61 	GtkWidget *hdr_entry;
     62 	GtkWidget *val_entry;
     63 	GtkWidget *preview;
     64 	GtkWidget *list_view;
     65 } customhdr;
     66 
     67 /* widget creating functions */
     68 static void prefs_custom_header_create	(void);
     69 
     70 static void prefs_custom_header_set_dialog		(PrefsAccount *ac);
     71 static void prefs_custom_header_set_list		(PrefsAccount *ac);
     72 static void prefs_custom_header_list_view_set_row	(PrefsAccount *ac);
     73 
     74 /* callback functions */
     75 static void prefs_custom_header_add_cb		(void);
     76 static void prefs_custom_header_val_from_file_cb(void);
     77 static void prefs_custom_header_delete_cb	(void);
     78 static void prefs_custom_header_up		(void);
     79 static void prefs_custom_header_down		(void);
     80 
     81 static void prefs_custom_header_ok		(void);
     82 static void prefs_custom_header_cancel		(void);
     83 static gint prefs_custom_header_deleted		(GtkWidget	*widget,
     84 						 GdkEventAny	*event,
     85 						 gpointer	 data);
     86 
     87 static GtkListStore* prefs_custom_header_create_data_store	(void);
     88 
     89 static void prefs_custom_header_list_view_insert_header	(GtkWidget *list_view,
     90 							 GtkTreeIter *row_iter,
     91 							 gchar *header,
     92 							 gpointer data);
     93 
     94 static GtkWidget *prefs_custom_header_list_view_create (void);
     95 
     96 static void prefs_custom_header_create_list_view_columns	(GtkWidget *list_view);
     97 
     98 static gboolean prefs_custom_header_selected	(GtkTreeSelection *selector,
     99 						 GtkTreeModel *model,
    100 						 GtkTreePath *path,
    101 						 gboolean currently_selected,
    102 						 gpointer data);
    103 
    104 
    105 static PrefsAccount *cur_ac = NULL;
    106 
    107 void prefs_custom_header_open(PrefsAccount *ac)
    108 {
    109 	if (!customhdr.window) {
    110 		prefs_custom_header_create();
    111 	}
    112 
    113 	manage_window_set_transient(GTK_WINDOW(customhdr.window));
    114 	gtk_widget_grab_focus(customhdr.ok_btn);
    115 
    116 	prefs_custom_header_set_dialog(ac);
    117 
    118 	cur_ac = ac;
    119 
    120 	gtk_widget_show(customhdr.window);
    121 	gtk_window_set_modal(GTK_WINDOW(customhdr.window), TRUE);
    122 }
    123 
    124 static void prefs_custom_header_create(void)
    125 {
    126 	GtkWidget *window;
    127 	GtkWidget *vbox;
    128 
    129 	GtkWidget *ok_btn;
    130 	GtkWidget *cancel_btn;
    131 
    132 	GtkWidget *confirm_area;
    133 
    134 	GtkWidget *vbox1;
    135 
    136 	GtkWidget *table1;
    137 	GtkWidget *hdr_label;
    138 	GtkWidget *hdr_combo;
    139 	GtkWidget *val_label;
    140 	GtkWidget *val_entry;
    141 	GtkWidget *val_btn;
    142 
    143 	GtkWidget *reg_hbox;
    144 	GtkWidget *btn_hbox;
    145 	GtkWidget *arrow;
    146 	GtkWidget *add_btn;
    147 	GtkWidget *del_btn;
    148 	GtkWidget *preview;
    149 
    150 	GtkWidget *ch_hbox;
    151 	GtkWidget *ch_scrolledwin;
    152 	GtkWidget *list_view;
    153 
    154 	GtkWidget *btn_vbox;
    155 	GtkWidget *up_btn;
    156 	GtkWidget *down_btn;
    157 
    158 	debug_print("Creating custom header setting window...\n");
    159 
    160 	window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "prefs_customheader");
    161 	gtk_container_set_border_width (GTK_CONTAINER (window), 8);
    162 	gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
    163 	gtk_window_set_resizable(GTK_WINDOW (window), TRUE);
    164 	gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
    165 
    166 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
    167 	gtk_widget_show (vbox);
    168 	gtk_container_add (GTK_CONTAINER (window), vbox);
    169 
    170 	gtkut_stock_button_set_create(&confirm_area, &cancel_btn, NULL, _("_Cancel"),
    171 				      &ok_btn, NULL, _("_OK"), NULL, NULL, NULL);
    172 	gtk_widget_show (confirm_area);
    173 	gtk_box_pack_end (GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
    174 	gtk_widget_grab_default (ok_btn);
    175 
    176 	gtk_window_set_title (GTK_WINDOW(window), _("Custom header configuration"));
    177 	MANAGE_WINDOW_SIGNALS_CONNECT (window);
    178 	g_signal_connect (G_OBJECT(window), "delete_event",
    179 			  G_CALLBACK(prefs_custom_header_deleted),
    180 			  NULL);
    181 	g_signal_connect (G_OBJECT(ok_btn), "clicked",
    182 			  G_CALLBACK(prefs_custom_header_ok), NULL);
    183 	g_signal_connect (G_OBJECT(cancel_btn), "clicked",
    184 			  G_CALLBACK(prefs_custom_header_cancel), NULL);
    185 
    186 	vbox1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, VSPACING);
    187 	gtk_widget_show (vbox1);
    188 	gtk_box_pack_start (GTK_BOX (vbox), vbox1, TRUE, TRUE, 0);
    189 	gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2);
    190 
    191 	table1 = gtk_grid_new();
    192 	gtk_widget_show (table1);
    193 	gtk_box_pack_start (GTK_BOX (vbox1), table1,
    194 			    FALSE, FALSE, 0);
    195 	gtk_grid_set_row_spacing(GTK_GRID(table1), 8);
    196 	gtk_grid_set_column_spacing(GTK_GRID(table1), 8);
    197 
    198 	hdr_label = gtk_label_new (_("Header"));
    199 	gtk_widget_show (hdr_label);
    200 	gtk_label_set_xalign(GTK_LABEL (hdr_label), 0.0);
    201 	gtk_grid_attach(GTK_GRID(table1), hdr_label, 0, 0, 1, 1);
    202 
    203 	hdr_combo = combobox_text_new(TRUE, "User-Agent", "Face", "X-Face",
    204 				      "X-Operating-System", NULL);
    205 	gtk_grid_attach(GTK_GRID(table1), hdr_combo, 0, 1, 1, 1);
    206 
    207 	val_label = gtk_label_new (_("Value"));
    208 	gtk_widget_show (val_label);
    209 	gtk_label_set_xalign(GTK_LABEL (val_label), 0.0);
    210 	gtk_grid_attach(GTK_GRID(table1), val_label, 1, 0, 1, 1);
    211 
    212 	val_entry = gtk_entry_new ();
    213 	gtk_widget_show (val_entry);
    214 	gtk_grid_attach(GTK_GRID(table1), val_entry, 1, 1, 1, 1);
    215 	gtk_widget_set_hexpand(val_entry, TRUE);
    216 	gtk_widget_set_halign(val_entry, GTK_ALIGN_FILL);
    217 
    218 	val_btn = gtkut_get_browse_file_btn(_("Bro_wse"));
    219 	gtk_widget_show (val_btn);
    220 	gtk_grid_attach(GTK_GRID(table1), val_btn, 2, 1, 1, 1);
    221 	g_signal_connect (G_OBJECT (val_btn), "clicked",
    222 			  G_CALLBACK (prefs_custom_header_val_from_file_cb),
    223 			  NULL);
    224 
    225 	/* add / delete */
    226 
    227 	reg_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
    228 	gtk_widget_show (reg_hbox);
    229 	gtk_box_pack_start (GTK_BOX (vbox1), reg_hbox, FALSE, FALSE, 0);
    230 
    231 	arrow = gtk_image_new_from_icon_name("pan-down-symbolic", GTK_ICON_SIZE_MENU);
    232 	gtk_widget_show (arrow);
    233 	gtk_box_pack_start (GTK_BOX (reg_hbox), arrow, FALSE, FALSE, 0);
    234 
    235 	btn_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
    236 	gtk_widget_show (btn_hbox);
    237 	gtk_box_pack_start (GTK_BOX (reg_hbox), btn_hbox, FALSE, FALSE, 0);
    238 
    239 	add_btn = gtkut_stock_button("list-add", _("_Add"));
    240 	gtk_widget_show (add_btn);
    241 	gtk_box_pack_start (GTK_BOX (btn_hbox), add_btn, FALSE, TRUE, 0);
    242 	g_signal_connect (G_OBJECT (add_btn), "clicked",
    243 			  G_CALLBACK (prefs_custom_header_add_cb),
    244 			  NULL);
    245 
    246 	del_btn = gtkut_stock_button("edit-delete", _("D_elete"));
    247 	gtk_widget_show (del_btn);
    248 	gtk_box_pack_start (GTK_BOX (btn_hbox), del_btn, FALSE, TRUE, 0);
    249 	g_signal_connect (G_OBJECT (del_btn), "clicked",
    250 			  G_CALLBACK (prefs_custom_header_delete_cb),
    251 			  NULL);
    252 
    253 
    254 	ch_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
    255 	gtk_widget_show (ch_hbox);
    256 	gtk_box_pack_start (GTK_BOX (vbox1), ch_hbox, TRUE, TRUE, 0);
    257 
    258 	ch_scrolledwin = gtk_scrolled_window_new (NULL, NULL);
    259 	gtk_widget_show (ch_scrolledwin);
    260 	gtk_box_pack_start (GTK_BOX (ch_hbox), ch_scrolledwin, TRUE, TRUE, 0);
    261 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (ch_scrolledwin),
    262 					GTK_POLICY_AUTOMATIC,
    263 					GTK_POLICY_AUTOMATIC);
    264 
    265 	list_view = prefs_custom_header_list_view_create();
    266 	gtk_widget_show (list_view);
    267 	gtk_container_add (GTK_CONTAINER (ch_scrolledwin), list_view);
    268 
    269 	btn_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
    270 	gtk_widget_show (btn_vbox);
    271 	gtk_box_pack_start (GTK_BOX (ch_hbox), btn_vbox, FALSE, FALSE, 0);
    272 
    273 	up_btn = gtkut_stock_button("go-up", _("_Up"));
    274 	gtk_widget_show (up_btn);
    275 	gtk_box_pack_start (GTK_BOX (btn_vbox), up_btn, FALSE, FALSE, 0);
    276 	g_signal_connect (G_OBJECT (up_btn), "clicked",
    277 			  G_CALLBACK (prefs_custom_header_up), NULL);
    278 
    279 	down_btn = gtkut_stock_button("go-down", _("_Down"));
    280 	gtk_widget_show (down_btn);
    281 	gtk_box_pack_start (GTK_BOX (btn_vbox), down_btn, FALSE, FALSE, 0);
    282 	g_signal_connect (G_OBJECT (down_btn), "clicked",
    283 			  G_CALLBACK (prefs_custom_header_down), NULL);
    284 
    285 	preview = gtk_image_new ();
    286 	gtk_widget_show (preview);
    287 	gtk_box_pack_start (GTK_BOX (btn_vbox), preview, FALSE, FALSE, 0);
    288 
    289 	gtk_widget_show_all(window);
    290 
    291 	customhdr.window     = window;
    292 	customhdr.ok_btn     = ok_btn;
    293 	customhdr.cancel_btn = cancel_btn;
    294 	customhdr.preview = preview;
    295 
    296 	customhdr.hdr_combo  = hdr_combo;
    297 	customhdr.hdr_entry  = gtk_bin_get_child(GTK_BIN((hdr_combo)));
    298 	customhdr.val_entry  = val_entry;
    299 
    300 	customhdr.list_view   = list_view;
    301 }
    302 
    303 void prefs_custom_header_read_config(PrefsAccount *ac)
    304 {
    305 	gchar *rcpath;
    306 	FILE *fp;
    307 	gchar buf[PREFSBUFSIZE];
    308 	CustomHeader *ch;
    309 
    310 	debug_print("Reading custom header configuration...\n");
    311 
    312 	rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
    313 			     CUSTOM_HEADER_RC, NULL);
    314 	if ((fp = g_fopen(rcpath, "rb")) == NULL) {
    315 		if (ENOENT != errno) FILE_OP_ERROR(rcpath, "g_fopen");
    316 		g_free(rcpath);
    317 		ac->customhdr_list = NULL;
    318 		return;
    319 	}
    320 	g_free(rcpath);
    321 
    322 	/* remove all previous headers list */
    323 	while (ac->customhdr_list != NULL) {
    324 		ch = (CustomHeader *)ac->customhdr_list->data;
    325 		ac->customhdr_list = g_slist_remove(ac->customhdr_list, ch);
    326 		custom_header_free(ch);
    327 	}
    328 
    329 	while (fgets(buf, sizeof(buf), fp) != NULL) {
    330 		ch = custom_header_read_str(buf);
    331 		if (ch) {
    332 			if (ch->account_id == ac->account_id) {
    333 				ac->customhdr_list =
    334 					g_slist_append(ac->customhdr_list, ch);
    335 			} else
    336 				custom_header_free(ch);
    337 		}
    338 	}
    339 
    340 	fclose(fp);
    341 }
    342 
    343 static void prefs_custom_header_write_config(PrefsAccount *ac)
    344 {
    345 	gchar *rcpath;
    346 	PrefFile *pfile;
    347 	GSList *cur;
    348 	gchar buf[PREFSBUFSIZE];
    349 	FILE * fp;
    350 	CustomHeader *ch;
    351 
    352 	GSList *all_hdrs = NULL;
    353 
    354 	debug_print("Writing custom header configuration...\n");
    355 
    356 	rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
    357 			     CUSTOM_HEADER_RC, NULL);
    358 
    359 	if ((fp = g_fopen(rcpath, "rb")) == NULL) {
    360 		if (ENOENT != errno) FILE_OP_ERROR(rcpath, "g_fopen");
    361 	} else {
    362 		all_hdrs = NULL;
    363 
    364 		while (fgets(buf, sizeof(buf), fp) != NULL) {
    365 			ch = custom_header_read_str(buf);
    366 			if (ch) {
    367 				if (ch->account_id != ac->account_id)
    368 					all_hdrs =
    369 						g_slist_append(all_hdrs, ch);
    370 				else
    371 					custom_header_free(ch);
    372 			}
    373 		}
    374 
    375 		fclose(fp);
    376 	}
    377 
    378 	if ((pfile = prefs_write_open(rcpath)) == NULL) {
    379 		g_warning("failed to write configuration to file");
    380 		g_free(rcpath);
    381 		return;
    382 	}
    383 
    384 	for (cur = all_hdrs; cur != NULL; cur = cur->next) {
    385  		CustomHeader *hdr = (CustomHeader *)cur->data;
    386 		gchar *chstr;
    387 
    388 		chstr = custom_header_get_str(hdr);
    389 		if (fputs(chstr, pfile->fp) == EOF ||
    390 		    fputc('\n', pfile->fp) == EOF) {
    391 			FILE_OP_ERROR(rcpath, "fputs || fputc");
    392 			prefs_file_close_revert(pfile);
    393 			g_free(rcpath);
    394 			g_free(chstr);
    395 			return;
    396 		}
    397 		g_free(chstr);
    398 	}
    399 
    400 	for (cur = ac->customhdr_list; cur != NULL; cur = cur->next) {
    401  		CustomHeader *hdr = (CustomHeader *)cur->data;
    402 		gchar *chstr;
    403 
    404 		chstr = custom_header_get_str(hdr);
    405 		if (fputs(chstr, pfile->fp) == EOF ||
    406 		    fputc('\n', pfile->fp) == EOF) {
    407 			FILE_OP_ERROR(rcpath, "fputs || fputc");
    408 			prefs_file_close_revert(pfile);
    409 			g_free(rcpath);
    410 			g_free(chstr);
    411 			return;
    412 		}
    413 		g_free(chstr);
    414 	}
    415 
    416 	g_free(rcpath);
    417 
    418  	while (all_hdrs != NULL) {
    419  		ch = (CustomHeader *)all_hdrs->data;
    420  		all_hdrs = g_slist_remove(all_hdrs, ch);
    421  		custom_header_free(ch);
    422  	}
    423 
    424 	if (prefs_file_close(pfile) < 0) {
    425 		g_warning("failed to write configuration to file");
    426 		return;
    427 	}
    428 }
    429 
    430 static void prefs_custom_header_set_dialog(PrefsAccount *ac)
    431 {
    432 	GtkListStore *store;
    433 	GSList *cur;
    434 
    435 	store = GTK_LIST_STORE(gtk_tree_view_get_model
    436 				(GTK_TREE_VIEW(customhdr.list_view)));
    437 	gtk_list_store_clear(store);
    438 
    439 	for (cur = ac->customhdr_list; cur != NULL; cur = cur->next) {
    440  		CustomHeader *ch = (CustomHeader *)cur->data;
    441 		gchar *ch_str;
    442 
    443 		ch_str = g_strdup_printf("%s: %s", ch->name,
    444 					 ch->value ? ch->value : "");
    445 
    446 		prefs_custom_header_list_view_insert_header
    447 			(customhdr.list_view, NULL, ch_str, ch);
    448 
    449 		g_free(ch_str);
    450 	}
    451 }
    452 
    453 static void prefs_custom_header_set_list(PrefsAccount *ac)
    454 {
    455 	CustomHeader *ch;
    456 	GtkTreeIter iter;
    457 	GtkListStore *store;
    458 
    459 	g_slist_free(ac->customhdr_list);
    460 	ac->customhdr_list = NULL;
    461 
    462 	store = GTK_LIST_STORE(gtk_tree_view_get_model
    463 				(GTK_TREE_VIEW(customhdr.list_view)));
    464 
    465 	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) {
    466 		do {
    467 			gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
    468 					   CUSTHDR_DATA, &ch,
    469 					   -1);
    470 			ac->customhdr_list = g_slist_append(ac->customhdr_list, ch);
    471 		} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store),
    472 						  &iter));
    473 	}
    474 }
    475 
    476 static void prefs_custom_header_list_view_set_row(PrefsAccount *ac)
    477 {
    478 	CustomHeader *ch;
    479 	const gchar *entry_text;
    480 	gchar *ch_str;
    481 
    482 	entry_text = gtk_entry_get_text(GTK_ENTRY(customhdr.hdr_entry));
    483 	if (entry_text[0] == '\0') {
    484 		alertpanel_error(_("Header name is not set."));
    485 		return;
    486 	}
    487 
    488 	while (*entry_text &&
    489 	       (*entry_text == '\n' || *entry_text == '\r' ||
    490 	        *entry_text == '\t' || *entry_text == ' '))
    491 		entry_text++;
    492 
    493 	if (strchr(entry_text, ':') != NULL) {
    494 		alertpanel_error(_("A colon (:) is not allowed in a custom header."));
    495 		return;
    496 	}
    497 
    498 	if (!custom_header_is_allowed(entry_text)) {
    499 		alertpanel_error(_("This Header name is not allowed as a custom header."));
    500 		return;
    501 	}
    502 
    503 	ch = g_new0(CustomHeader, 1);
    504 
    505 	ch->account_id = ac->account_id;
    506 
    507 	ch->name = g_strdup(entry_text);
    508 	unfold_line(ch->name);
    509 	g_strstrip(ch->name);
    510 	gtk_entry_set_text(GTK_ENTRY(customhdr.hdr_entry), ch->name);
    511 
    512 	entry_text = gtk_entry_get_text(GTK_ENTRY(customhdr.val_entry));
    513 	while (*entry_text &&
    514 	       (*entry_text == '\n' || *entry_text == '\r' ||
    515 	        *entry_text == '\t' || *entry_text == ' '))
    516 		entry_text++;
    517 
    518 	if (entry_text[0] != '\0') {
    519 		ch->value = g_strdup(entry_text);
    520 		unfold_line(ch->value);
    521 		g_strstrip(ch->value);
    522 		gtk_entry_set_text(GTK_ENTRY(customhdr.val_entry), ch->value);
    523 	}
    524 
    525 	ch_str = g_strdup_printf("%s: %s", ch->name,
    526 				 ch->value ? ch->value : "");
    527 
    528 	prefs_custom_header_list_view_insert_header
    529 		(customhdr.list_view, NULL, ch_str, ch);
    530 
    531 	g_free(ch_str);
    532 
    533 	prefs_custom_header_set_list(cur_ac);
    534 
    535 }
    536 
    537 static void prefs_custom_header_val_from_file_cb(void)
    538 {
    539 	gchar *filename = NULL;
    540 	gchar *contents = NULL;
    541 
    542 	filename = filesel_select_file_open(_("Choose a text file"), NULL);
    543 	if (!filename)
    544 		return;
    545 
    546 	contents = file_read_to_str(filename);
    547 	if (strchr(contents, '\n') || strchr(contents,'\r')) {
    548 		alertpanel_error(_("This file contains newlines."));
    549 		g_free(contents);
    550 		g_free(filename);
    551 		return;
    552 	}
    553 
    554 	if (contents && strlen(contents))
    555 		gtk_entry_set_text(GTK_ENTRY(customhdr.val_entry), contents);
    556 
    557 	g_free(contents);
    558 	g_free(filename);
    559 }
    560 
    561 static void prefs_custom_header_add_cb(void)
    562 {
    563 	prefs_custom_header_list_view_set_row(cur_ac);
    564 }
    565 
    566 static void prefs_custom_header_delete_cb(void)
    567 {
    568 	GtkTreeIter sel;
    569 	GtkTreeModel *model;
    570 	CustomHeader *ch;
    571 
    572 	if (!gtk_tree_selection_get_selected(gtk_tree_view_get_selection
    573 				(GTK_TREE_VIEW(customhdr.list_view)),
    574 				&model, &sel))
    575 		return;
    576 
    577 	if (alertpanel(_("Delete header"),
    578 		       _("Do you really want to delete this header?"),
    579 		       NULL, _("_Cancel"), "edit-delete", _("_Delete"), NULL, NULL,
    580 		       ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
    581 		return;
    582 
    583 	gtk_tree_model_get(model, &sel,
    584 			   CUSTHDR_DATA, &ch,
    585 			   -1);
    586 	gtk_list_store_remove(GTK_LIST_STORE(model), &sel);
    587 
    588 	cur_ac->customhdr_list = g_slist_remove(cur_ac->customhdr_list, ch);
    589 
    590 	custom_header_free(ch);
    591 }
    592 
    593 static void prefs_custom_header_up(void)
    594 {
    595 	GtkTreePath *prev, *sel;
    596 	GtkTreeIter isel;
    597 	GtkListStore *store = NULL;
    598 	GtkTreeModel *model = NULL;
    599 	GtkTreeIter iprev;
    600 
    601 	if (!gtk_tree_selection_get_selected
    602 		(gtk_tree_view_get_selection
    603 			(GTK_TREE_VIEW(customhdr.list_view)),
    604 		 &model,
    605 		 &isel))
    606 		return;
    607 	store = (GtkListStore *)model;
    608 	sel = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &isel);
    609 	if (!sel)
    610 		return;
    611 
    612 	/* no move if we're at row 0... */
    613 	prev = gtk_tree_path_copy(sel);
    614 	if (!gtk_tree_path_prev(prev)) {
    615 		gtk_tree_path_free(prev);
    616 		gtk_tree_path_free(sel);
    617 		return;
    618 	}
    619 
    620 	gtk_tree_model_get_iter(GTK_TREE_MODEL(store),
    621 				&iprev, prev);
    622 	gtk_tree_path_free(sel);
    623 	gtk_tree_path_free(prev);
    624 
    625 	gtk_list_store_swap(store, &iprev, &isel);
    626 	prefs_custom_header_set_list(cur_ac);
    627 }
    628 
    629 static void prefs_custom_header_down(void)
    630 {
    631 	GtkListStore *store = NULL;
    632 	GtkTreeModel *model = NULL;
    633 	GtkTreeIter next, sel;
    634 
    635 	if (!gtk_tree_selection_get_selected
    636 		(gtk_tree_view_get_selection
    637 			(GTK_TREE_VIEW(customhdr.list_view)),
    638 		 &model,
    639 		 &sel))
    640 		return;
    641 	store = (GtkListStore *)model;
    642 	next = sel;
    643 	if (!gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &next))
    644 		return;
    645 
    646 	gtk_list_store_swap(store, &next, &sel);
    647 	prefs_custom_header_set_list(cur_ac);
    648 }
    649 
    650 static void prefs_custom_header_ok(void)
    651 {
    652 	prefs_custom_header_write_config(cur_ac);
    653 	gtk_widget_hide(customhdr.window);
    654 	gtk_window_set_modal(GTK_WINDOW(customhdr.window), FALSE);
    655 }
    656 
    657 static void prefs_custom_header_cancel(void)
    658 {
    659 	prefs_custom_header_read_config(cur_ac);
    660 	gtk_widget_hide(customhdr.window);
    661 	gtk_window_set_modal(GTK_WINDOW(customhdr.window), FALSE);
    662 }
    663 
    664 static gint prefs_custom_header_deleted(GtkWidget *widget, GdkEventAny *event,
    665 					gpointer data)
    666 {
    667 	prefs_custom_header_cancel();
    668 	return TRUE;
    669 }
    670 
    671 static GtkListStore* prefs_custom_header_create_data_store(void)
    672 {
    673 	return gtk_list_store_new(N_CUSTHDR_COLUMNS,
    674 				  G_TYPE_STRING,
    675 				  G_TYPE_POINTER,
    676 				  -1);
    677 }
    678 
    679 static void prefs_custom_header_list_view_insert_header(GtkWidget *list_view,
    680 							GtkTreeIter *row_iter,
    681 							gchar *header,
    682 							gpointer data)
    683 {
    684 	GtkTreeIter iter;
    685 	GtkListStore *list_store = GTK_LIST_STORE(gtk_tree_view_get_model
    686 					(GTK_TREE_VIEW(list_view)));
    687 
    688 	if (row_iter == NULL) {
    689 		/* append new */
    690 		gtk_list_store_append(list_store, &iter);
    691 		gtk_list_store_set(list_store, &iter,
    692 				   CUSTHDR_STRING, header,
    693 				   CUSTHDR_DATA,   data,
    694 				   -1);
    695 	} else {
    696 		/* change existing */
    697 		CustomHeader *old_data;
    698 
    699 		gtk_tree_model_get(GTK_TREE_MODEL(list_store), row_iter,
    700 				   CUSTHDR_DATA, &old_data,
    701 				   -1);
    702 
    703 		custom_header_free(old_data);
    704 
    705 		gtk_list_store_set(list_store, row_iter,
    706 				   CUSTHDR_STRING, header,
    707 				   CUSTHDR_DATA, data,
    708 				   -1);
    709 	}
    710 }
    711 
    712 static GtkWidget *prefs_custom_header_list_view_create(void)
    713 {
    714 	GtkTreeView *list_view;
    715 	GtkTreeSelection *selector;
    716 	GtkTreeModel *model;
    717 
    718 	model = GTK_TREE_MODEL(prefs_custom_header_create_data_store());
    719 	list_view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(model));
    720 	g_object_unref(model);
    721 
    722 	gtk_tree_view_set_reorderable(list_view, TRUE);
    723 
    724 	selector = gtk_tree_view_get_selection(list_view);
    725 	gtk_tree_selection_set_mode(selector, GTK_SELECTION_BROWSE);
    726 	gtk_tree_selection_set_select_function(selector, prefs_custom_header_selected,
    727 					       NULL, NULL);
    728 
    729 	/* create the columns */
    730 	prefs_custom_header_create_list_view_columns(GTK_WIDGET(list_view));
    731 
    732 	return GTK_WIDGET(list_view);
    733 }
    734 
    735 static void prefs_custom_header_create_list_view_columns(GtkWidget *list_view)
    736 {
    737 	GtkTreeViewColumn *column;
    738 	GtkCellRenderer *renderer;
    739 
    740 	renderer = gtk_cell_renderer_text_new();
    741 	column = gtk_tree_view_column_new_with_attributes
    742 		(_("Current custom headers"),
    743 		 renderer,
    744 		 "text", CUSTHDR_STRING,
    745 		 NULL);
    746 	gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);
    747 }
    748 
    749 #define ENTRY_SET_TEXT(entry, str) \
    750 	gtk_entry_set_text(GTK_ENTRY(entry), str ? str : "")
    751 
    752 static gboolean prefs_custom_header_selected(GtkTreeSelection *selector,
    753 					     GtkTreeModel *model,
    754 					     GtkTreePath *path,
    755 					     gboolean currently_selected,
    756 					     gpointer data)
    757 {
    758 	GtkTreeIter iter;
    759 	CustomHeader *ch;
    760 	CustomHeader default_ch = { 0, "", NULL };
    761 
    762 	if (currently_selected)
    763 		return TRUE;
    764 
    765 	if (!gtk_tree_model_get_iter(model, &iter, path))
    766 		return TRUE;
    767 
    768 	gtk_tree_model_get(model, &iter,
    769 			   CUSTHDR_DATA, &ch,
    770 			   -1);
    771 
    772 	if (!ch) ch = &default_ch;
    773 
    774 	ENTRY_SET_TEXT(customhdr.hdr_entry, ch->name);
    775 	ENTRY_SET_TEXT(customhdr.val_entry, ch->value);
    776 	gtk_widget_hide(customhdr.preview);
    777 	return TRUE;
    778 }
    779 
    780 #undef ENTRY_SET_TEXT