1 /* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
2 /* Balsa E-Mail Client
3 * Copyright (C) 1997-2013 Stuart Parmenter and others,
4 * See the file AUTHORS for a list.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 */
21
22 #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H
23 # include "config.h"
24 #endif /* HAVE_CONFIG_H */
25 #include "toolbar-prefs.h"
26
27 #include <string.h>
28 #include <glib/gi18n.h>
29
30 #include "balsa-app.h"
31 #include "balsa-icons.h"
32 #include "main-window.h"
33 #include "message-window.h"
34 #include "sendmsg-window.h"
35 #include "toolbar-factory.h"
36
37 #if HAVE_MACOSX_DESKTOP
38 # include "macosx-helpers.h"
39 #endif
40
41 #ifndef MAX
42 #define MAX(a, b) ((a) > (b) ? (a) : (b))
43 #endif
44
45 /* Enumeration for GtkTreeModel columns. */
46 enum {
47 TP_TEXT_COLUMN,
48 TP_ICON_COLUMN,
49 TP_ITEM_COLUMN,
50 TP_N_COLUMNS
51 };
52
53 static GtkWidget *customize_widget;
54
55 /* Structure associated with each notebook page. */
56 typedef struct ToolbarPage_ ToolbarPage;
57 struct ToolbarPage_ {
58 BalsaToolbarModel *model;
59 GtkWidget *available;
60 GtkWidget *current;
61 GtkWidget *toolbar;
62 GtkWidget *add_button;
63 GtkWidget *remove_button;
64 GtkWidget *back_button;
65 GtkWidget *forward_button;
66 GtkWidget *standard_button;
67 };
68
69 /* Callbacks. */
70 static void tp_dialog_response_cb(GtkDialog * dialog, gint response,
71 gpointer data);
72 static void add_button_cb(GtkWidget *, ToolbarPage * page);
73 static void remove_button_cb(GtkWidget *, ToolbarPage * page);
74 static void back_button_cb(GtkWidget *, ToolbarPage * page);
75 static void forward_button_cb(GtkWidget *, ToolbarPage * page);
76 static void wrap_toggled_cb(GtkWidget * widget, GtkNotebook * notebook);
77 static void available_selection_changed_cb(GtkTreeSelection * selection,
78 ToolbarPage * page);
79 static void current_selection_changed_cb(GtkTreeSelection * selection,
80 ToolbarPage * page);
81 static void available_row_activated_cb(GtkTreeView * treeview,
82 GtkTreeIter * arg1,
83 GtkTreePath * arg2,
84 ToolbarPage * page);
85 static void current_row_activated_cb(GtkTreeView * treeview,
86 GtkTreeIter * arg1,
87 GtkTreePath * arg2,
88 ToolbarPage * page);
89
90 /* Helpers. */
91 static GtkWidget *create_toolbar_page(BalsaToolbarModel * model,
92 GtkUIManager * ui_manager);
93 static GtkWidget *tp_list_new(void);
94 static gboolean tp_list_iter_is_first(GtkWidget * list, GtkTreeIter * iter);
95 static gboolean tp_list_iter_is_last(GtkWidget * list, GtkTreeIter * iter);
96 static void tp_page_refresh_available(ToolbarPage * page);
97 static void tp_page_refresh_current(ToolbarPage * page);
98 static void tp_page_refresh_preview(ToolbarPage * page);
99 static void tp_page_swap_rows(ToolbarPage * page, gboolean forward);
100 static void tp_page_add_selected(ToolbarPage * page);
101 static void tp_page_remove_selected(ToolbarPage * page);
102 static void tp_store_set(GtkListStore * store, GtkTreeIter * iter,
103 gint item);
104 static void replace_nl_with_space(char* str);
105
106 /* Public methods. */
107
108 /* create the toolbar-customization dialog
109 */
110 void
customize_dialog_cb(GtkWidget * widget,gpointer data)111 customize_dialog_cb(GtkWidget * widget, gpointer data)
112 {
113 GtkWidget *notebook;
114 GtkWidget *child;
115 GtkWidget *option_frame;
116 GtkWidget *option_box;
117 GtkWidget *wrap_button;
118 GtkWidget *active_window = data;
119 BalsaToolbarModel *model;
120 BalsaToolbarType type;
121 GtkUIManager * ui_manager;
122 GtkWidget *content_area;
123
124 /* There can only be one */
125 if (customize_widget) {
126 gtk_window_present(GTK_WINDOW(customize_widget));
127 return;
128 }
129
130 customize_widget =
131 gtk_dialog_new_with_buttons(_("Customize Toolbars"),
132 GTK_WINDOW(active_window),
133 GTK_DIALOG_DESTROY_WITH_PARENT,
134 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
135 GTK_STOCK_HELP, GTK_RESPONSE_HELP,
136 NULL);
137 #if HAVE_MACOSX_DESKTOP
138 libbalsa_macosx_menu_for_parent(customize_widget, GTK_WINDOW(active_window));
139 #endif
140 g_object_add_weak_pointer(G_OBJECT(customize_widget),
141 (gpointer) & customize_widget);
142 g_signal_connect(G_OBJECT(customize_widget), "response",
143 G_CALLBACK(tp_dialog_response_cb), NULL);
144
145 notebook = gtk_notebook_new();
146 content_area =
147 gtk_dialog_get_content_area(GTK_DIALOG(customize_widget));
148 gtk_container_add(GTK_CONTAINER(content_area), notebook);
149
150 gtk_window_set_wmclass(GTK_WINDOW(customize_widget), "customize",
151 "Balsa");
152 gtk_window_set_default_size(GTK_WINDOW(customize_widget), 600, 440);
153
154 /* The order of pages must be consistent with the BalsaToolbarType
155 * enum. */
156 model = balsa_window_get_toolbar_model();
157 ui_manager = balsa_window_ui_manager_new(NULL);
158 child = create_toolbar_page(model, ui_manager);
159 g_object_unref(ui_manager);
160 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), child,
161 gtk_label_new(_("Main window")));
162
163 model = sendmsg_window_get_toolbar_model();
164 ui_manager = sendmsg_window_ui_manager_new(NULL);
165 child = create_toolbar_page(model, ui_manager);
166 g_object_unref(ui_manager);
167 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), child,
168 gtk_label_new(_("Compose window")));
169
170 model = message_window_get_toolbar_model();
171 ui_manager = message_window_ui_manager_new(NULL);
172 child = create_toolbar_page(model, ui_manager);
173 g_object_unref(ui_manager);
174 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), child,
175 gtk_label_new(_("Message window")));
176
177 option_frame = gtk_frame_new(_("Toolbar options"));
178 gtk_container_set_border_width(GTK_CONTAINER(option_frame), 6);
179 gtk_box_pack_start(GTK_BOX(content_area), option_frame, FALSE, FALSE, 0);
180
181 option_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
182 gtk_container_set_border_width(GTK_CONTAINER(option_box), 6);
183 gtk_container_add(GTK_CONTAINER(option_frame), option_box);
184
185 wrap_button =
186 gtk_check_button_new_with_mnemonic(_("_Wrap button labels"));
187 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wrap_button),
188 balsa_app.toolbar_wrap_button_text);
189 g_signal_connect(G_OBJECT(wrap_button), "toggled",
190 G_CALLBACK(wrap_toggled_cb), notebook);
191 gtk_box_pack_start(GTK_BOX(option_box), wrap_button, FALSE, FALSE, 0);
192
193 gtk_widget_show_all(customize_widget);
194
195 /* Now that the pages are shown, we can switch to the page
196 * corresponding to the toolbar that the user clicked on. */
197 type =
198 GPOINTER_TO_INT(g_object_get_data
199 (G_OBJECT(widget), BALSA_TOOLBAR_MODEL_TYPE));
200 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), type);
201 }
202
203 /* get_toolbar_button_index:
204 id - button id
205 returns -1 on failure.
206 */
207 int
get_toolbar_button_index(const char * id)208 get_toolbar_button_index(const char *id)
209 {
210 int i;
211
212 g_return_val_if_fail(id, -1);
213
214 for(i=0; i<toolbar_button_count; i++) {
215 if(!strcmp(id, toolbar_buttons[i].pixmap_id))
216 return i;
217 }
218 return -1;
219 }
220
221 /* Callbacks. */
222
223 #define BALSA_KEY_TOOLBAR_PAGE "balsa-toolbar-page"
224
225 /* Callback for the wrap_buttons' "toggled" signal. */
226 static void
wrap_toggled_cb(GtkWidget * widget,GtkNotebook * notebook)227 wrap_toggled_cb(GtkWidget * widget, GtkNotebook * notebook)
228 {
229 gint i;
230 GtkWidget *child;
231 ToolbarPage *page;
232
233 balsa_app.toolbar_wrap_button_text =
234 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
235
236 for (i = 0; (child = gtk_notebook_get_nth_page(notebook, i)); i++) {
237 page = g_object_get_data(G_OBJECT(child), BALSA_KEY_TOOLBAR_PAGE);
238 balsa_toolbar_model_changed(page->model);
239 }
240 }
241
242 /* Button callbacks: each makes the appropriate change to the
243 * page->current GtkTreeView, then refreshes the page's
244 * BalsaToolbarModel and GtkToolbar; add_button_cb and remove_button_cb
245 * also refresh the page's available GtkTreeView.
246 */
247 static void
back_button_cb(GtkWidget * widget,ToolbarPage * page)248 back_button_cb(GtkWidget * widget, ToolbarPage * page)
249 {
250 tp_page_swap_rows(page, FALSE);
251 }
252
253 static void
forward_button_cb(GtkWidget * widget,ToolbarPage * page)254 forward_button_cb(GtkWidget * widget, ToolbarPage * page)
255 {
256 tp_page_swap_rows(page, TRUE);
257 }
258
259 static void
add_button_cb(GtkWidget * widget,ToolbarPage * page)260 add_button_cb(GtkWidget *widget, ToolbarPage * page)
261 {
262 tp_page_add_selected(page);
263 }
264
265 static void
remove_button_cb(GtkWidget * widget,ToolbarPage * page)266 remove_button_cb(GtkWidget *widget, ToolbarPage * page)
267 {
268 tp_page_remove_selected(page);
269 }
270
271 static void
standard_button_cb(GtkWidget * widget,ToolbarPage * page)272 standard_button_cb(GtkWidget *widget, ToolbarPage * page)
273 {
274 balsa_toolbar_model_clear(page->model);
275 gtk_widget_set_sensitive(page->standard_button, FALSE);
276 tp_page_refresh_available(page);
277 tp_page_refresh_current(page);
278 balsa_toolbar_model_changed(page->model);
279 }
280
281 static void
style_button_cb(GtkWidget * widget,ToolbarPage * page)282 style_button_cb(GtkWidget *widget, ToolbarPage * page)
283 {
284 gboolean handled;
285
286 g_signal_emit_by_name(page->toolbar, "popup-menu", &handled);
287 }
288
289 /* Callback for the "row-activated" signal for the available list. */
290 static void
available_row_activated_cb(GtkTreeView * treeview,GtkTreeIter * arg1,GtkTreePath * arg2,ToolbarPage * page)291 available_row_activated_cb(GtkTreeView * treeview, GtkTreeIter * arg1,
292 GtkTreePath * arg2, ToolbarPage * page)
293 {
294 tp_page_add_selected(page);
295 }
296
297 /* Callback for the selection "changed" signal for the available list. */
298 static void
available_selection_changed_cb(GtkTreeSelection * selection,ToolbarPage * page)299 available_selection_changed_cb(GtkTreeSelection * selection,
300 ToolbarPage * page)
301 {
302 gtk_widget_set_sensitive(page->add_button,
303 gtk_tree_selection_get_selected(selection,
304 NULL, NULL));
305 }
306
307 /* Callback for the "row-activated" signal for the current list. */
308 static void
current_row_activated_cb(GtkTreeView * treeview,GtkTreeIter * arg1,GtkTreePath * arg2,ToolbarPage * page)309 current_row_activated_cb(GtkTreeView * treeview, GtkTreeIter * arg1,
310 GtkTreePath * arg2, ToolbarPage * page)
311 {
312 tp_page_remove_selected(page);
313 }
314
315 /* Callback for the selection "changed" signal for the destination list. */
316 static void
current_selection_changed_cb(GtkTreeSelection * selection,ToolbarPage * page)317 current_selection_changed_cb(GtkTreeSelection * selection, ToolbarPage * page)
318 {
319 GtkTreeModel *model;
320 GtkTreeIter iter;
321 gboolean remove = FALSE;
322 gboolean back = FALSE;
323 gboolean forward = FALSE;
324
325 if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
326 remove = TRUE;
327 back =
328 !tp_list_iter_is_first(page->current,
329 &iter);
330 forward =
331 !tp_list_iter_is_last(page->current,
332 &iter);
333 }
334 gtk_widget_set_sensitive(page->remove_button, remove);
335 gtk_widget_set_sensitive(page->back_button, back);
336 gtk_widget_set_sensitive(page->forward_button, forward);
337 }
338
339 /* Callback for the "response" signal of the dialog. */
340 static void
tp_dialog_response_cb(GtkDialog * dialog,gint response,gpointer data)341 tp_dialog_response_cb(GtkDialog * dialog, gint response, gpointer data)
342 {
343 GdkScreen *screen;
344 GError *err = NULL;
345
346 switch (response) {
347 case GTK_RESPONSE_DELETE_EVENT:
348 case GTK_RESPONSE_CLOSE:
349 gtk_widget_destroy(GTK_WIDGET(dialog));
350 break;
351 case GTK_RESPONSE_HELP:
352 screen = gtk_widget_get_screen(GTK_WIDGET(dialog));
353 gtk_show_uri(screen, "ghelp:balsa?toolbar-prefs",
354 gtk_get_current_event_time(), &err);
355 if (err) {
356 balsa_information(LIBBALSA_INFORMATION_WARNING,
357 _("Error displaying toolbar help: %s\n"),
358 err->message);
359 g_error_free(err);
360 }
361 break;
362 default:
363 break;
364 }
365 }
366
367 /* Helpers. */
368
369 /* Create a page for the main notebook.
370 */
371 static GtkWidget*
create_toolbar_page(BalsaToolbarModel * model,GtkUIManager * ui_manager)372 create_toolbar_page(BalsaToolbarModel * model, GtkUIManager * ui_manager)
373 {
374 GtkWidget *outer_box;
375 GtkWidget *toolbar_frame, *toolbar_scroll;
376 GtkWidget *toolbar_ctlbox;
377 GtkWidget *lower_ctlbox, *button_box, *move_button_box, *center_button_box;
378 GtkWidget *list_frame, *list_scroll;
379 GtkWidget *destination_frame, *destination_scroll;
380 GtkWidget *style_button;
381 ToolbarPage *page;
382 GtkTreeSelection *selection;
383
384 page = g_new(ToolbarPage, 1);
385 page->model = model;
386
387 /* The "window itself" */
388 outer_box=gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
389 g_object_set_data_full(G_OBJECT(outer_box), BALSA_KEY_TOOLBAR_PAGE,
390 page, g_free);
391
392 /* Preview display */
393 toolbar_frame=gtk_frame_new(_("Preview"));
394 gtk_container_set_border_width(GTK_CONTAINER(toolbar_frame), 5);
395 gtk_box_pack_start(GTK_BOX(outer_box), toolbar_frame, FALSE, FALSE, 0);
396
397 toolbar_ctlbox=gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
398 gtk_container_add(GTK_CONTAINER(toolbar_frame), toolbar_ctlbox);
399 gtk_container_set_border_width(GTK_CONTAINER(toolbar_ctlbox), 5);
400
401 /* The ui-manager has actions but no ui, so we add an empty toolbar. */
402 gtk_ui_manager_add_ui_from_string(ui_manager,
403 "<ui>"
404 " <toolbar name='Toolbar'/>"
405 "</ui>",
406 -1, NULL);
407
408 /* The preview is an actual, fully functional toolbar */
409 page->toolbar = balsa_toolbar_new(model, ui_manager);
410 gtk_widget_set_sensitive(page->toolbar, FALSE);
411
412 /* embedded in a scrolled_window */
413 toolbar_scroll=gtk_scrolled_window_new(NULL, NULL);
414 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(toolbar_scroll),
415 GTK_POLICY_AUTOMATIC,
416 GTK_POLICY_NEVER);
417
418 gtk_box_pack_start(GTK_BOX(toolbar_ctlbox), toolbar_scroll,
419 TRUE, TRUE, 0);
420
421 #if GTK_CHECK_VERSION(3, 8, 0)
422 gtk_container_add(GTK_CONTAINER(toolbar_scroll), page->toolbar);
423 #else /* GTK_CHECK_VERSION(3, 8, 0) */
424 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(toolbar_scroll),
425 page->toolbar);
426 #endif /* GTK_CHECK_VERSION(3, 8, 0) */
427
428 /* Button box */
429 button_box = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
430 gtk_box_pack_start(GTK_BOX(toolbar_ctlbox), button_box, FALSE, FALSE, 0);
431
432 /* Standard button */
433 page->standard_button =
434 gtk_button_new_with_mnemonic(_("_Restore toolbar to standard buttons"));
435 gtk_container_add(GTK_CONTAINER(button_box), page->standard_button);
436
437 /* Style button */
438 style_button = gtk_button_new_with_mnemonic(_("Toolbar _style..."));
439 gtk_container_add(GTK_CONTAINER(button_box), style_button);
440
441 /* Done with preview */
442
443 /* Box for lower half of window */
444 lower_ctlbox=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
445 gtk_container_set_border_width(GTK_CONTAINER(lower_ctlbox), 5);
446
447 gtk_box_pack_start(GTK_BOX(outer_box), lower_ctlbox, TRUE, TRUE, 0);
448
449 /* A list to show the available items */
450 list_scroll=gtk_scrolled_window_new(NULL, NULL);
451
452 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(list_scroll),
453 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
454
455 list_frame=gtk_frame_new(_("Available buttons"));
456 page->available = tp_list_new();
457
458 gtk_box_pack_start(GTK_BOX(lower_ctlbox), list_frame,
459 TRUE, TRUE, 0);
460 gtk_container_add(GTK_CONTAINER(list_frame), list_scroll);
461 gtk_container_add(GTK_CONTAINER(list_scroll), page->available);
462
463 /* Done with available list */
464
465 /* Another list to show the current tools */
466 destination_scroll=gtk_scrolled_window_new(NULL, NULL);
467
468 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(destination_scroll),
469 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
470
471 destination_frame=gtk_frame_new(_("Current toolbar"));
472 page->current = tp_list_new();
473
474 /* Done with destination list */
475
476 /* Button box */
477 center_button_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
478 gtk_box_set_homogeneous(GTK_BOX(center_button_box), TRUE);
479
480 button_box=gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
481
482 gtk_box_pack_start(GTK_BOX(lower_ctlbox), center_button_box,
483 FALSE, FALSE, 0);
484
485 gtk_box_pack_start(GTK_BOX(center_button_box), button_box,
486 FALSE, FALSE, 0);
487
488 page->back_button =
489 balsa_stock_button_with_label(GTK_STOCK_GO_UP, _("Up"));
490 gtk_box_pack_start(GTK_BOX(button_box), page->back_button, FALSE, FALSE, 0);
491
492 move_button_box=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
493 gtk_box_pack_start(GTK_BOX(button_box), move_button_box, FALSE, FALSE, 0);
494
495 page->remove_button =
496 balsa_stock_button_with_label(GTK_STOCK_GO_BACK, "-");
497 gtk_box_pack_start(GTK_BOX(move_button_box), page->remove_button,
498 FALSE, FALSE, 0);
499
500 page->add_button =
501 balsa_stock_button_with_label(GTK_STOCK_GO_FORWARD, "+");
502 gtk_box_pack_start(GTK_BOX(move_button_box), page->add_button, FALSE, FALSE, 0);
503
504 page->forward_button =
505 balsa_stock_button_with_label(GTK_STOCK_GO_DOWN, _("Down"));
506 gtk_box_pack_start(GTK_BOX(button_box), page->forward_button, FALSE, FALSE, 0);
507
508 /* Pack destination list */
509 gtk_box_pack_start(GTK_BOX(lower_ctlbox), destination_frame, TRUE, TRUE, 0);
510 gtk_container_add(GTK_CONTAINER(destination_frame), destination_scroll);
511 gtk_container_add(GTK_CONTAINER(destination_scroll), page->current);
512
513 /* UI signals */
514 g_signal_connect(G_OBJECT(page->available), "row-activated",
515 G_CALLBACK(available_row_activated_cb), page);
516 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(page->available));
517 g_signal_connect(G_OBJECT(selection), "changed",
518 G_CALLBACK(available_selection_changed_cb), page);
519
520 g_signal_connect(G_OBJECT(page->current), "row-activated",
521 G_CALLBACK(current_row_activated_cb), page);
522 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(page->current));
523 g_signal_connect(G_OBJECT(selection), "changed",
524 G_CALLBACK(current_selection_changed_cb), page);
525
526 g_signal_connect(G_OBJECT(page->add_button), "clicked",
527 G_CALLBACK(add_button_cb), page);
528 g_signal_connect(G_OBJECT(page->remove_button), "clicked",
529 G_CALLBACK(remove_button_cb), page);
530 g_signal_connect(G_OBJECT(page->forward_button), "clicked",
531 G_CALLBACK(forward_button_cb), page);
532 g_signal_connect(G_OBJECT(page->back_button), "clicked",
533 G_CALLBACK(back_button_cb), page);
534
535 g_signal_connect(G_OBJECT(page->standard_button), "clicked",
536 G_CALLBACK(standard_button_cb), page);
537 g_signal_connect(G_OBJECT(style_button), "clicked",
538 G_CALLBACK(style_button_cb), page);
539
540 gtk_widget_set_sensitive(page->add_button, FALSE);
541 gtk_widget_set_sensitive(page->remove_button, FALSE);
542 gtk_widget_set_sensitive(page->back_button, FALSE);
543 gtk_widget_set_sensitive(page->forward_button, FALSE);
544 gtk_widget_set_sensitive(page->standard_button,
545 !balsa_toolbar_model_is_standard(model));
546
547 tp_page_refresh_available(page);
548 tp_page_refresh_current(page);
549
550 return outer_box;
551 }
552
553 /* Refresh the page's available GtkTreeView.
554 */
555 static void
tp_page_refresh_available(ToolbarPage * page)556 tp_page_refresh_available(ToolbarPage * page)
557 {
558 GtkTreeSelection *selection =
559 gtk_tree_view_get_selection(GTK_TREE_VIEW(page->available));
560 GtkTreeModel *model;
561 GtkTreeIter iter;
562 GtkTreePath *path;
563 GHashTable *legal = balsa_toolbar_model_get_legal(page->model);
564 GSList *current = balsa_toolbar_model_get_current(page->model);
565 int item;
566
567 /* save currently selected path, or point path to the first row */
568 if (gtk_tree_selection_get_selected(selection, &model, &iter))
569 path = gtk_tree_model_get_path(model, &iter);
570 else {
571 path = gtk_tree_path_new();
572 gtk_tree_path_down(path);
573 }
574 gtk_list_store_clear(GTK_LIST_STORE(model));
575
576 for (item = 0; item < toolbar_button_count; item++) {
577 if (item > 0
578 && (!g_hash_table_lookup(legal,
579 toolbar_buttons[item].pixmap_id)
580 || g_slist_find_custom(current,
581 toolbar_buttons[item].pixmap_id,
582 (GCompareFunc) strcmp)))
583 continue;
584
585 gtk_list_store_append(GTK_LIST_STORE(model), &iter);
586 tp_store_set(GTK_LIST_STORE(model), &iter, item);
587 }
588
589 if (gtk_tree_model_get_iter(model, &iter, path)
590 || gtk_tree_path_prev(path)) {
591 gtk_tree_selection_select_path(selection, path);
592 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(page->available),
593 path, NULL, FALSE, 0, 0);
594 }
595 gtk_tree_path_free(path);
596 }
597
598 /* Refresh the page's current GtkTreeView.
599 */
600 static void
tp_page_refresh_current(ToolbarPage * page)601 tp_page_refresh_current(ToolbarPage * page)
602 {
603 GtkTreeSelection *selection =
604 gtk_tree_view_get_selection(GTK_TREE_VIEW(page->current));
605 GtkTreeModel *model;
606 GtkTreeIter iter;
607 GtkTreePath *path;
608 int item;
609 GSList *list;
610
611 /* save currently selected path, or point path to the first row */
612 if (gtk_tree_selection_get_selected(selection, &model, &iter))
613 path = gtk_tree_model_get_path(model, &iter);
614 else {
615 path = gtk_tree_path_new();
616 gtk_tree_path_down(path);
617 }
618 gtk_list_store_clear(GTK_LIST_STORE(model));
619
620 for (list = balsa_toolbar_model_get_current(page->model); list;
621 list = g_slist_next(list)) {
622 item = get_toolbar_button_index(list->data);
623 if (item < 0)
624 continue;
625
626 gtk_list_store_append(GTK_LIST_STORE(model), &iter);
627 tp_store_set(GTK_LIST_STORE(model), &iter, item);
628 }
629
630 if (gtk_tree_model_get_iter(model, &iter, path)
631 || gtk_tree_path_prev(path)) {
632 gtk_tree_selection_select_path(selection, path);
633 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(page->current),
634 path, NULL, FALSE, 0, 0);
635 }
636 gtk_tree_path_free(path);
637 }
638
639 /* Refresh a page after its destination TreeView has changed.
640 */
641 static void
tp_page_refresh_preview(ToolbarPage * page)642 tp_page_refresh_preview(ToolbarPage * page)
643 {
644 GtkTreeModel *model =
645 gtk_tree_view_get_model(GTK_TREE_VIEW(page->current));
646 GtkTreeIter iter;
647 gboolean valid;
648
649 balsa_toolbar_model_clear(page->model);
650 for (valid = gtk_tree_model_get_iter_first(model, &iter);
651 valid;
652 valid = gtk_tree_model_iter_next(model, &iter)) {
653 gint item;
654
655 gtk_tree_model_get(model, &iter, TP_ITEM_COLUMN, &item, -1);
656 if (item >= 0 && item < toolbar_button_count)
657 balsa_toolbar_model_insert_icon(page->model,
658 toolbar_buttons[item].pixmap_id,
659 -1);
660 }
661 }
662
663 /* Create a GtkTreeView for an icon list.
664 */
665 static GtkWidget *
tp_list_new(void)666 tp_list_new(void)
667 {
668 GtkListStore *list_store;
669 GtkTreeView *view;
670 GtkCellRenderer *renderer;
671 GtkTreeViewColumn *column;
672
673 list_store = gtk_list_store_new(TP_N_COLUMNS,
674 G_TYPE_STRING, /* TP_TEXT_COLUMN */
675 GDK_TYPE_PIXBUF, /* TP_ICON_COLUMN */
676 G_TYPE_INT /* TP_ITEM_COLUMN */
677 );
678 view = GTK_TREE_VIEW(gtk_tree_view_new_with_model
679 (GTK_TREE_MODEL(list_store)));
680 g_object_unref(list_store);
681
682 renderer = gtk_cell_renderer_text_new();
683 column =
684 gtk_tree_view_column_new_with_attributes(NULL, renderer, "text",
685 TP_TEXT_COLUMN, NULL);
686 gtk_tree_view_append_column(view, column);
687
688 renderer = gtk_cell_renderer_pixbuf_new();
689 column =
690 gtk_tree_view_column_new_with_attributes(NULL, renderer, "pixbuf",
691 TP_ICON_COLUMN, NULL);
692 gtk_tree_view_append_column(view, column);
693
694 gtk_tree_view_set_headers_visible(view, FALSE);
695 return GTK_WIDGET(view);
696 }
697
698 /* Test whether the iter addresses the first row.
699 */
700 static gboolean
tp_list_iter_is_first(GtkWidget * list,GtkTreeIter * iter)701 tp_list_iter_is_first(GtkWidget * list, GtkTreeIter * iter)
702 {
703 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
704 GtkTreePath *path = gtk_tree_model_get_path(model, iter);
705 gboolean ret_val = !gtk_tree_path_prev(path);
706 gtk_tree_path_free(path);
707 return ret_val;
708 }
709
710 /* Test whether the iter addresses the last row.
711 */
712 static gboolean
tp_list_iter_is_last(GtkWidget * list,GtkTreeIter * iter)713 tp_list_iter_is_last(GtkWidget * list, GtkTreeIter * iter)
714 {
715 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
716 GtkTreeIter tmp_iter = *iter;
717 return !gtk_tree_model_iter_next(model, &tmp_iter);
718 }
719
720 /* Swap the currently selected row in page->current
721 * with the next or previous row.
722 */
723 static void
tp_page_swap_rows(ToolbarPage * page,gboolean forward)724 tp_page_swap_rows(ToolbarPage * page, gboolean forward)
725 {
726 GtkTreeModel *model =
727 gtk_tree_view_get_model(GTK_TREE_VIEW(page->current));
728 GtkTreeSelection *selection =
729 gtk_tree_view_get_selection(GTK_TREE_VIEW(page->current));
730 GtkTreeIter iter;
731 GtkTreeIter tmp_iter;
732 GtkTreePath *tmp_path;
733 gint item, tmp_item;
734
735 selection =
736 gtk_tree_view_get_selection(GTK_TREE_VIEW(page->current));
737 if (!gtk_tree_selection_get_selected(selection, &model, &iter))
738 return;
739
740 if (forward) {
741 tmp_iter = iter;
742 if (!gtk_tree_model_iter_next(model, &tmp_iter))
743 return;
744 tmp_path = gtk_tree_model_get_path(model, &tmp_iter);
745 } else {
746 tmp_path = gtk_tree_model_get_path(model, &iter);
747 if (!gtk_tree_path_prev(tmp_path)) {
748 gtk_tree_path_free(tmp_path);
749 return;
750 }
751 gtk_tree_model_get_iter(model, &tmp_iter, tmp_path);
752 }
753
754 gtk_tree_model_get(model, &iter, TP_ITEM_COLUMN, &item, -1);
755 gtk_tree_model_get(model, &tmp_iter, TP_ITEM_COLUMN, &tmp_item, -1);
756 tp_store_set(GTK_LIST_STORE(model), &tmp_iter, item);
757 tp_store_set(GTK_LIST_STORE(model), &iter, tmp_item);
758
759 gtk_tree_selection_select_path(selection, tmp_path);
760 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(page->current),
761 tmp_path, NULL, FALSE, 0, 0);
762 gtk_tree_path_free(tmp_path);
763
764 gtk_widget_set_sensitive(page->standard_button, TRUE);
765 tp_page_refresh_preview(page);
766 balsa_toolbar_model_changed(page->model);
767 }
768
769 /* Add an item to a GtkTreeView's GtkListStore.
770 */
771 static void
tp_store_set(GtkListStore * store,GtkTreeIter * iter,gint item)772 tp_store_set(GtkListStore * store, GtkTreeIter * iter, gint item)
773 {
774 GdkPixbuf *pixbuf;
775 gchar *text;
776
777 text = g_strdup(balsa_toolbar_button_text(item));
778 replace_nl_with_space(text);
779 pixbuf =
780 (item > 0 ? gtk_widget_render_icon_pixbuf(customize_widget,
781 toolbar_buttons[item].
782 pixmap_id,
783 GTK_ICON_SIZE_LARGE_TOOLBAR)
784 : NULL);
785 gtk_list_store_set(store, iter,
786 TP_TEXT_COLUMN, text,
787 TP_ICON_COLUMN, pixbuf,
788 TP_ITEM_COLUMN, item,
789 -1);
790 g_free(text);
791 if (pixbuf)
792 g_object_unref(pixbuf);
793 }
794
795 /* Add an item to the current list.
796 */
797 static void
tp_page_add_selected(ToolbarPage * page)798 tp_page_add_selected(ToolbarPage * page)
799 {
800 gint item = 0;
801 GtkTreeSelection *selection;
802 GtkTreeModel *model;
803 GtkTreeIter iter;
804 GtkTreeIter sibling;
805 GtkTreePath *path;
806
807 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(page->available));
808 if (!gtk_tree_selection_get_selected(selection, &model, &iter))
809 return;
810 gtk_tree_model_get(model, &iter, TP_ITEM_COLUMN, &item, -1);
811
812 selection =
813 gtk_tree_view_get_selection(GTK_TREE_VIEW(page->current));
814 if (gtk_tree_selection_get_selected(selection, &model, &sibling))
815 gtk_list_store_insert_before(GTK_LIST_STORE(model), &iter,
816 &sibling);
817 else
818 gtk_list_store_append(GTK_LIST_STORE(model), &iter);
819
820 tp_store_set(GTK_LIST_STORE(model), &iter, item);
821
822 path = gtk_tree_model_get_path(model, &iter);
823 gtk_tree_selection_select_path(selection, path);
824 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(page->current), path, NULL,
825 FALSE, 0, 0);
826 gtk_tree_path_free(path);
827
828 gtk_widget_set_sensitive(page->standard_button, TRUE);
829 tp_page_refresh_preview(page);
830 tp_page_refresh_available(page);
831 balsa_toolbar_model_changed(page->model);
832 }
833
834 /* Remove an item from the page's current list.
835 */
836 static void
tp_page_remove_selected(ToolbarPage * page)837 tp_page_remove_selected(ToolbarPage * page)
838 {
839 GtkTreeSelection *selection =
840 gtk_tree_view_get_selection(GTK_TREE_VIEW(page->current));
841 GtkTreeModel *model;
842 GtkTreeIter iter;
843 GtkTreePath *path;
844
845 if (!gtk_tree_selection_get_selected(selection, &model, &iter))
846 return;
847 path = gtk_tree_model_get_path(model, &iter);
848
849 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
850
851 if (gtk_tree_model_get_iter(model, &iter, path)
852 || gtk_tree_path_prev(path)) {
853 gtk_tree_selection_select_path(selection, path);
854 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(page->current),
855 path, NULL, FALSE, 0, 0);
856 }
857 gtk_tree_path_free(path);
858
859 gtk_widget_set_sensitive(page->standard_button, TRUE);
860 tp_page_refresh_preview(page);
861 tp_page_refresh_available(page);
862 balsa_toolbar_model_changed(page->model);
863 }
864
865 /* Unwrap text.
866 */
867 static void
replace_nl_with_space(char * str)868 replace_nl_with_space(char* str)
869 {
870 while(*str) {
871 if(*str == '\n')
872 *str=' ';
873 str++;
874 }
875 }
876