foldersel.c (21660B)
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 <gdk/gdkkeysyms.h> 24 #include <gtk/gtk.h> 25 #include <stdio.h> 26 #include <unistd.h> 27 #include <string.h> 28 #include <sys/stat.h> 29 #include <sys/types.h> 30 #include <fcntl.h> 31 #include <errno.h> 32 33 #include "main.h" 34 #include "utils.h" 35 #include "gtkutils.h" 36 #include "stock_pixmap.h" 37 #include "foldersel.h" 38 #include "alertpanel.h" 39 #include "manage_window.h" 40 #include "folderview.h" 41 #include "inputdialog.h" 42 #include "folder.h" 43 #include "prefs_common.h" 44 #include "folder_item_prefs.h" 45 46 enum { 47 FOLDERSEL_FOLDERNAME, 48 FOLDERSEL_FOLDERITEM, 49 FOLDERSEL_PIXBUF, 50 FOLDERSEL_PIXBUF_OPEN, 51 FOLDERSEL_FOREGROUND, 52 FOLDERSEL_BOLD, 53 N_FOLDERSEL_COLUMNS 54 }; 55 56 typedef struct _FolderItemSearch FolderItemSearch; 57 58 struct _FolderItemSearch 59 { 60 FolderItem *item; 61 GtkTreePath *path; 62 GtkTreeIter iter; 63 }; 64 65 static GdkPixbuf *folder_pixbuf = NULL; 66 static GdkPixbuf *folderopen_pixbuf = NULL; 67 static GdkPixbuf *foldernoselect_pixbuf = NULL; 68 static GdkPixbuf *foldernoselectopen_pixbuf = NULL; 69 70 static GtkWidget *window; 71 static GtkWidget *treeview; 72 static GtkWidget *statusbar; 73 static gint statusbar_cid; 74 static GtkWidget *ok_button; 75 static GtkWidget *cancel_button; 76 static GtkWidget *new_button; 77 static gboolean root_selectable; 78 79 static FolderItem *folder_item; 80 static FolderItem *selected_item; 81 82 static GtkTreeStore *tree_store; 83 84 static gboolean cancelled; 85 static gboolean finished; 86 87 static void foldersel_create (const gchar *title, 88 FolderSelectionType type); 89 static void foldersel_init (void); 90 91 static void foldersel_append_item (GtkTreeStore *store, 92 FolderItem *item, 93 GtkTreeIter *iter, 94 GtkTreeIter *parent); 95 96 static void foldersel_set_tree (Folder *cur_folder, 97 FolderSelectionType type); 98 99 static gboolean foldersel_selected (GtkTreeSelection *selection, 100 GtkTreeModel *model, 101 GtkTreePath *path, 102 gboolean currently_selected, 103 gpointer data); 104 105 static void foldersel_ok (GtkButton *button, 106 gpointer data); 107 static void foldersel_cancel (GtkButton *button, 108 gpointer data); 109 static void foldersel_new_folder (GtkButton *button, 110 gpointer data); 111 112 static void foldersel_tree_activated (GtkTreeView *treeview, 113 GtkTreePath *path, 114 GtkTreeViewColumn *column, 115 gpointer data); 116 117 static gint delete_event (GtkWidget *widget, 118 GdkEventAny *event, 119 gpointer data); 120 static gboolean key_pressed (GtkWidget *widget, 121 GdkEventKey *event, 122 gpointer data); 123 124 static gint foldersel_folder_name_compare (GtkTreeModel *model, 125 GtkTreeIter *a, 126 GtkTreeIter *b, 127 gpointer context); 128 129 static gboolean tree_view_folder_item_func (GtkTreeModel *model, 130 GtkTreePath *path, 131 GtkTreeIter *iter, 132 FolderItemSearch *data); 133 134 FolderItem *foldersel_folder_sel(Folder *cur_folder, FolderSelectionType type, 135 const gchar *default_folder, gboolean can_sel_mailbox, 136 const gchar *title) 137 { 138 selected_item = NULL; 139 root_selectable = can_sel_mailbox; 140 141 if (!window) { 142 foldersel_create(title, type); 143 foldersel_init(); 144 } 145 146 foldersel_set_tree(cur_folder, type); 147 148 /* select current */ 149 if (folder_item) { 150 FolderItemSearch fis; 151 152 fis.item = folder_item; 153 fis.path = NULL; 154 155 /* find matching model entry */ 156 gtk_tree_model_foreach 157 (GTK_TREE_MODEL(tree_store), 158 (GtkTreeModelForeachFunc)tree_view_folder_item_func, 159 &fis); 160 161 if (fis.path) { 162 GtkTreeSelection *selection; 163 164 selection = gtk_tree_view_get_selection 165 (GTK_TREE_VIEW(treeview)); 166 gtk_tree_selection_select_iter(selection, &fis.iter); 167 gtk_tree_view_set_cursor(GTK_TREE_VIEW(treeview), 168 fis.path, NULL, FALSE); 169 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(treeview), 170 fis.path, 171 NULL, TRUE, 0.5, 0.0); 172 gtk_tree_path_free(fis.path); 173 } else 174 gtk_tree_view_scroll_to_point 175 (GTK_TREE_VIEW(treeview), 0, 0); 176 } else 177 gtk_tree_view_scroll_to_point(GTK_TREE_VIEW(treeview), 0, 0); 178 179 gtk_widget_grab_focus(ok_button); 180 gtk_widget_grab_focus(treeview); 181 182 gtk_window_present(GTK_WINDOW(window)); 183 manage_window_set_transient(GTK_WINDOW(window)); 184 gtk_window_set_modal(GTK_WINDOW(window), TRUE); 185 186 cancelled = finished = FALSE; 187 188 while (finished == FALSE) 189 gtk_main_iteration(); 190 191 gtk_widget_destroy(window); 192 window = NULL; 193 194 if (!cancelled && 195 selected_item && (selected_item->path || root_selectable)) { 196 folder_item = selected_item; 197 return folder_item; 198 } else 199 return NULL; 200 } 201 202 static gboolean foldersel_search_name_func(GtkTreeModel *model, gint column, 203 const gchar *key, GtkTreeIter *iter, gpointer search_data) 204 { 205 gchar *store_string = NULL; 206 FolderItem *item; 207 gboolean retval; 208 209 if (column == FOLDERSEL_FOLDERNAME) { 210 /* get the name of the FolderItem, not the displayed string */ 211 gtk_tree_model_get(model, iter, 212 FOLDERSEL_FOLDERITEM, &item, -1); 213 store_string = folder_item_get_name(item); 214 } else { 215 gtk_tree_model_get(model, iter, column, &store_string, -1); 216 } 217 218 if (!store_string) 219 return FALSE; 220 221 if (!key) 222 retval = FALSE; 223 else 224 retval = (strcasestr(store_string, key) == NULL); 225 226 g_free(store_string); 227 228 return retval; 229 } 230 231 static void foldersel_size_allocate_cb(GtkWidget *widget, 232 GtkAllocation *allocation) 233 { 234 cm_return_if_fail(allocation != NULL); 235 236 gtk_window_get_size(GTK_WINDOW(widget), 237 &prefs_common.folderselwin_width, &prefs_common.folderselwin_height); 238 } 239 240 static void foldersel_create(const gchar *title, FolderSelectionType type) 241 { 242 GtkWidget *vbox; 243 GtkWidget *scrolledwin; 244 GtkWidget *confirm_area; 245 GtkWidget *label; 246 GtkTreeViewColumn *column; 247 GtkCellRenderer *renderer; 248 GtkTreeSelection *selection; 249 static GdkGeometry geometry; 250 251 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "foldersel"); 252 gtk_window_set_title(GTK_WINDOW(window),_("Select folder")); 253 gtk_container_set_border_width(GTK_CONTAINER(window), 4); 254 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); 255 gtk_window_set_resizable(GTK_WINDOW(window), TRUE); 256 gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG); 257 258 gtk_widget_realize(window); 259 g_signal_connect(G_OBJECT(window), "delete_event", 260 G_CALLBACK(delete_event), NULL); 261 g_signal_connect(G_OBJECT(window), "key_press_event", 262 G_CALLBACK(key_pressed), NULL); 263 g_signal_connect(G_OBJECT(window), "size_allocate", 264 G_CALLBACK(foldersel_size_allocate_cb), NULL); 265 MANAGE_WINDOW_SIGNALS_CONNECT(window); 266 267 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4); 268 gtk_container_add(GTK_CONTAINER(window), vbox); 269 270 if (title != NULL) { 271 label = gtk_label_new(title); 272 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); 273 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); 274 } 275 276 scrolledwin = gtk_scrolled_window_new(NULL, NULL); 277 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), 278 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); 279 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), 280 GTK_SHADOW_IN); 281 gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0); 282 283 tree_store = gtk_tree_store_new(N_FOLDERSEL_COLUMNS, 284 G_TYPE_STRING, 285 G_TYPE_POINTER, 286 GDK_TYPE_PIXBUF, 287 GDK_TYPE_PIXBUF, 288 GDK_TYPE_RGBA, 289 G_TYPE_INT); 290 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(tree_store), 291 FOLDERSEL_FOLDERNAME, 292 foldersel_folder_name_compare, 293 NULL, NULL); 294 295 treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(tree_store)); 296 g_object_unref(G_OBJECT(tree_store)); 297 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE); 298 gtk_tree_view_set_enable_tree_lines(GTK_TREE_VIEW(treeview), FALSE); 299 gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), 300 FOLDERSEL_FOLDERNAME); 301 if (prefs_common.folder_search_wildcard) 302 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(treeview), 303 foldersel_search_name_func, NULL, NULL); 304 305 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); 306 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE); 307 gtk_tree_selection_set_select_function(selection, foldersel_selected, 308 NULL, NULL); 309 310 g_signal_connect(G_OBJECT(treeview), "row-activated", 311 G_CALLBACK(foldersel_tree_activated), NULL); 312 gtk_container_add(GTK_CONTAINER(scrolledwin), treeview); 313 314 column = gtk_tree_view_column_new(); 315 gtk_tree_view_column_set_spacing(column, 2); 316 renderer = gtk_cell_renderer_pixbuf_new(); 317 gtk_tree_view_column_pack_start(column, renderer, FALSE); 318 gtk_tree_view_column_set_attributes 319 (column, renderer, 320 "pixbuf", FOLDERSEL_PIXBUF, 321 "pixbuf-expander-open", FOLDERSEL_PIXBUF_OPEN, 322 "pixbuf-expander-closed", FOLDERSEL_PIXBUF, 323 NULL); 324 325 /* create text renderer */ 326 renderer = gtk_cell_renderer_text_new(); 327 gtk_cell_renderer_set_padding(renderer, 0, 0); 328 gtk_tree_view_column_pack_start(column, renderer, TRUE); 329 gtk_tree_view_column_set_attributes 330 (column, renderer, 331 "text", FOLDERSEL_FOLDERNAME, 332 "foreground-rgba", FOLDERSEL_FOREGROUND, 333 "weight", FOLDERSEL_BOLD, 334 NULL); 335 g_object_set(G_OBJECT(renderer), "weight", PANGO_WEIGHT_BOLD, NULL); 336 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); 337 338 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); 339 340 statusbar = gtk_statusbar_new(); 341 statusbar_cid = gtk_statusbar_get_context_id( 342 GTK_STATUSBAR(statusbar), "Select Folder Dialog" ); 343 gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0); 344 345 gtkut_stock_button_set_create(&confirm_area, 346 &new_button, NULL, _("_New"), 347 &cancel_button, NULL, (type == FOLDER_SEL_SAVE? 348 _("Don't save") : _("_Cancel")), 349 &ok_button, NULL, _("_OK")); 350 351 gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0); 352 gtk_widget_grab_default(ok_button); 353 354 g_signal_connect(G_OBJECT(ok_button), "clicked", 355 G_CALLBACK(foldersel_ok), NULL); 356 g_signal_connect(G_OBJECT(cancel_button), "clicked", 357 G_CALLBACK(foldersel_cancel), NULL); 358 g_signal_connect(G_OBJECT(new_button), "clicked", 359 G_CALLBACK(foldersel_new_folder), NULL); 360 361 if (!geometry.min_height) { 362 geometry.min_width = 300; 363 geometry.min_height = 360; 364 } 365 366 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL, &geometry, 367 GDK_HINT_MIN_SIZE); 368 gtk_window_set_default_size(GTK_WINDOW(window), 369 prefs_common.folderselwin_width, 370 prefs_common.folderselwin_height); 371 372 gtk_widget_show_all(vbox); 373 } 374 375 static void foldersel_init(void) 376 { 377 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE, &folder_pixbuf); 378 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN, &folderopen_pixbuf); 379 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE, &foldernoselect_pixbuf); 380 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_OPEN, &foldernoselectopen_pixbuf); 381 } 382 383 void foldersel_reflect_prefs_pixmap_theme(void) 384 { 385 if (folder_pixbuf) 386 g_object_unref(folder_pixbuf); 387 if (folderopen_pixbuf) 388 g_object_unref(folderopen_pixbuf); 389 if (foldernoselect_pixbuf) 390 g_object_unref(foldernoselect_pixbuf); 391 if (foldernoselectopen_pixbuf) 392 g_object_unref(foldernoselectopen_pixbuf); 393 foldersel_init(); 394 } 395 396 static void foldersel_append_item(GtkTreeStore *store, FolderItem *item, 397 GtkTreeIter *iter, GtkTreeIter *parent) 398 { 399 gchar *name, *tmpname; 400 GdkPixbuf *pixbuf, *pixbuf_open; 401 gboolean use_color; 402 PangoWeight weight = PANGO_WEIGHT_NORMAL; 403 GdkRGBA *foreground = NULL; 404 GdkRGBA color_noselect = {COLOR_DIM, COLOR_DIM, COLOR_DIM, 1.0}; 405 406 name = folder_item_get_name(item); 407 408 if (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0) { 409 tmpname = g_strdup_printf("%s (%d)", name, item->total_msgs); 410 } else if (item->unread_msgs > 0) { 411 tmpname = g_strdup_printf("%s (%d)", name, item->unread_msgs); 412 } else 413 tmpname = g_strdup(name); 414 415 g_free(name); 416 name = tmpname; 417 418 pixbuf = item->no_select ? foldernoselect_pixbuf : folder_pixbuf; 419 pixbuf_open = 420 item->no_select ? foldernoselectopen_pixbuf : folderopen_pixbuf; 421 422 if (folder_has_parent_of_type(item, F_DRAFT) || 423 folder_has_parent_of_type(item, F_OUTBOX) || 424 folder_has_parent_of_type(item, F_TRASH)) { 425 use_color = FALSE; 426 } else if (folder_has_parent_of_type(item, F_QUEUE)) { 427 use_color = (item->total_msgs > 0); 428 if (item->total_msgs > 0) 429 weight = PANGO_WEIGHT_BOLD; 430 } else { 431 if (item->unread_msgs > 0) 432 weight = PANGO_WEIGHT_BOLD; 433 use_color = (item->new_msgs > 0); 434 } 435 436 if (item->no_select) 437 foreground = &color_noselect; 438 else if (use_color) 439 foreground = &prefs_common.color[COL_NEW]; 440 441 /* insert this node */ 442 gtk_tree_store_append(store, iter, parent); 443 gtk_tree_store_set(store, iter, 444 FOLDERSEL_FOLDERNAME, name, 445 FOLDERSEL_FOLDERITEM, item, 446 FOLDERSEL_PIXBUF, pixbuf, 447 FOLDERSEL_PIXBUF_OPEN, pixbuf_open, 448 FOLDERSEL_FOREGROUND, foreground, 449 FOLDERSEL_BOLD, weight, 450 -1); 451 452 g_free(name); 453 } 454 455 static void foldersel_insert_gnode_in_store(GtkTreeStore *store, GNode *node, 456 GtkTreeIter *parent) 457 { 458 FolderItem *item; 459 GtkTreeIter child; 460 GNode *iter; 461 462 cm_return_if_fail(node != NULL); 463 cm_return_if_fail(node->data != NULL); 464 cm_return_if_fail(store != NULL); 465 466 item = FOLDER_ITEM(node->data); 467 foldersel_append_item(store, item, &child, parent); 468 469 /* insert its children (this node as parent) */ 470 for (iter = node->children; iter != NULL; iter = iter->next) 471 foldersel_insert_gnode_in_store(store, iter, &child); 472 } 473 474 static void foldersel_set_tree(Folder *cur_folder, FolderSelectionType type) 475 { 476 Folder *folder; 477 GList *list; 478 479 for (list = folder_get_list(); list != NULL; list = list->next) { 480 folder = FOLDER(list->data); 481 cm_return_if_fail(folder != NULL); 482 483 if (type != FOLDER_SEL_ALL) { 484 if (FOLDER_TYPE(folder) == F_NEWS) 485 continue; 486 } 487 488 if (cur_folder && (cur_folder->klass != folder->klass 489 && g_strcmp0(cur_folder->name, folder->name) != 0)) 490 continue; 491 492 foldersel_insert_gnode_in_store(tree_store, folder->node, NULL); 493 } 494 495 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(tree_store), 496 FOLDERSEL_FOLDERNAME, 497 GTK_SORT_ASCENDING); 498 499 gtk_tree_view_expand_all(GTK_TREE_VIEW(treeview)); 500 } 501 502 #include "localfolder.h" 503 static gboolean foldersel_selected(GtkTreeSelection *selection, 504 GtkTreeModel *model, GtkTreePath *path, 505 gboolean currently_selected, gpointer data) 506 { 507 GtkTreeIter iter; 508 FolderItem *item = NULL; 509 510 if (currently_selected) 511 return TRUE; 512 513 if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path)) 514 return TRUE; 515 516 gtk_tree_model_get(GTK_TREE_MODEL(tree_store), &iter, 517 FOLDERSEL_FOLDERITEM, &item, -1); 518 519 selected_item = item; 520 if (selected_item && selected_item->path) { 521 gchar *id; 522 id = folder_item_get_identifier(selected_item); 523 gtk_statusbar_push(GTK_STATUSBAR(statusbar), statusbar_cid, id); 524 g_free(id); 525 } else if (root_selectable && selected_item && selected_item->folder && 526 (FOLDER_TYPE(selected_item->folder) == F_MH || 527 FOLDER_TYPE(selected_item->folder) == F_MBOX || 528 FOLDER_TYPE(selected_item->folder) == F_IMAP)) { 529 gchar *id = folder_get_identifier(selected_item->folder); 530 gtk_statusbar_push(GTK_STATUSBAR(statusbar), statusbar_cid, id); 531 g_free(id); 532 } else 533 gtk_statusbar_push(GTK_STATUSBAR(statusbar), statusbar_cid, ""); 534 535 return TRUE; 536 } 537 538 static void foldersel_ok(GtkButton *button, gpointer data) 539 { 540 finished = TRUE; 541 } 542 543 static void foldersel_cancel(GtkButton *button, gpointer data) 544 { 545 cancelled = TRUE; 546 finished = TRUE; 547 } 548 549 static void foldersel_new_folder(GtkButton *button, gpointer data) 550 { 551 FolderItem *new_item; 552 gchar *new_folder; 553 gchar *disp_name; 554 gchar *p; 555 GtkTreeIter selected, new_child; 556 GtkTreePath *selected_p, *new_child_p; 557 GtkTreeStore *store; 558 GtkTreeModel *model; 559 GtkTreeSelection *selection; 560 561 if (!selected_item || FOLDER_TYPE(selected_item->folder) == F_NEWS) 562 return; 563 564 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); 565 if (!gtk_tree_selection_get_selected(selection, &model, &selected)) 566 return; 567 store = GTK_TREE_STORE(model); 568 569 new_folder = input_dialog_with_checkbtn(_("New folder"), 570 _("Input the name of new folder:"), 571 _("NewFolder"), 572 _("Inherit properties and processing rules from parent folder"), 573 &(prefs_common.inherit_folder_props)); 574 575 if (!new_folder) return; 576 AUTORELEASE_STR(new_folder, {g_free(new_folder); return;}); 577 578 p = strchr(new_folder, G_DIR_SEPARATOR); 579 if ((p && FOLDER_TYPE(selected_item->folder) != F_IMAP) || 580 (p && FOLDER_TYPE(selected_item->folder) == F_IMAP && 581 *(p + 1) != '\0')) { 582 alertpanel_error(_("'%c' can't be included in folder name."), 583 G_DIR_SEPARATOR); 584 return; 585 } 586 587 if (FOLDER_TYPE(selected_item->folder) != F_IMAP && 588 FOLDER_TYPE(selected_item->folder) != F_NEWS && 589 !folder_local_name_ok(new_folder)) 590 return; 591 592 disp_name = trim_string(new_folder, 32); 593 AUTORELEASE_STR(disp_name, {g_free(disp_name); return;}); 594 595 /* find whether the directory already exists */ 596 if (folder_find_child_item_by_name(selected_item, new_folder)) { 597 alertpanel_error(_("The folder '%s' already exists."), 598 disp_name); 599 return; 600 } 601 602 new_item = folder_create_folder(selected_item, new_folder); 603 if (!new_item) { 604 alertpanel_error(_("Can't create the folder '%s'."), disp_name); 605 return; 606 } 607 608 if (prefs_common.inherit_folder_props) { 609 folder_item_prefs_copy_prefs(selected_item, new_item); 610 } 611 612 /* add new child */ 613 foldersel_append_item(store, new_item, &new_child, &selected); 614 615 selected_p = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &selected); 616 new_child_p = gtk_tree_model_get_path(GTK_TREE_MODEL(store), 617 &new_child); 618 619 gtk_tree_view_expand_row(GTK_TREE_VIEW(treeview), selected_p, FALSE); 620 gtk_tree_selection_select_iter(selection, &new_child); 621 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(treeview), new_child_p, 622 NULL, TRUE, 0.5, 0.0); 623 gtk_tree_path_free(new_child_p); 624 gtk_tree_path_free(selected_p); 625 626 folder_write_list(); 627 } 628 629 static void foldersel_tree_activated(GtkTreeView *treeview, GtkTreePath *path, 630 GtkTreeViewColumn *column, gpointer data) 631 { 632 gtk_button_clicked(GTK_BUTTON(ok_button)); 633 } 634 635 static gint delete_event(GtkWidget *widget, GdkEventAny *event, gpointer data) 636 { 637 foldersel_cancel(NULL, NULL); 638 return TRUE; 639 } 640 641 static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data) 642 { 643 if (!event) 644 return FALSE; 645 646 GtkTreePath *path = NULL; 647 gtk_tree_view_get_cursor(GTK_TREE_VIEW(treeview), &path, NULL); 648 if (path == NULL) 649 return FALSE; 650 651 switch (event->keyval) { 652 case GDK_KEY_Left: 653 if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(treeview), path)) { 654 gtk_tree_view_collapse_row(GTK_TREE_VIEW(treeview), path); 655 } else { 656 gtk_tree_path_up(path); 657 gtk_tree_view_set_cursor(GTK_TREE_VIEW(treeview), path, NULL, FALSE); 658 } 659 return TRUE; 660 break; 661 case GDK_KEY_Right: 662 if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(treeview), path)) { 663 gtk_tree_view_expand_row(GTK_TREE_VIEW(treeview), path, FALSE); 664 } else { 665 gtk_tree_path_down(path); 666 gtk_tree_view_set_cursor(GTK_TREE_VIEW(treeview), path, NULL, FALSE); 667 } 668 return TRUE; 669 break; 670 } 671 672 gtk_tree_path_free(path); 673 674 return FALSE; 675 } 676 677 static gint foldersel_folder_name_compare(GtkTreeModel *model, GtkTreeIter *a, 678 GtkTreeIter *b, gpointer context) 679 { 680 gchar *str_a = NULL, *str_b = NULL; 681 gint val = 0; 682 FolderItem *item_a = NULL, *item_b = NULL; 683 GtkTreeIter parent; 684 685 gtk_tree_model_get(model, a, FOLDERSEL_FOLDERITEM, &item_a, -1); 686 gtk_tree_model_get(model, b, FOLDERSEL_FOLDERITEM, &item_b, -1); 687 688 /* no sort for root folder */ 689 if (!gtk_tree_model_iter_parent(GTK_TREE_MODEL(model), &parent, a)) 690 return 0; 691 692 /* if both a and b are special folders, sort them according to 693 * their types (which is in-order). Note that this assumes that 694 * there are no multiple folders of a special type. */ 695 if (item_a->stype != F_NORMAL && item_b->stype != F_NORMAL) 696 return item_a->stype - item_b->stype; 697 698 /* if b is normal folder, and a is not, b is smaller (ends up 699 * lower in the list) */ 700 if (item_a->stype != F_NORMAL && item_b->stype == F_NORMAL) 701 return item_b->stype - item_a->stype; 702 703 /* if b is special folder, and a is not, b is larger (ends up 704 * higher in the list) */ 705 if (item_a->stype == F_NORMAL && item_b->stype != F_NORMAL) 706 return item_b->stype - item_a->stype; 707 708 /* XXX g_utf8_collate_key() comparisons may speed things 709 * up when having large lists of folders */ 710 gtk_tree_model_get(model, a, FOLDERSEL_FOLDERNAME, &str_a, -1); 711 gtk_tree_model_get(model, b, FOLDERSEL_FOLDERNAME, &str_b, -1); 712 713 /* otherwise just compare the folder names */ 714 val = g_utf8_collate(str_a, str_b); 715 716 g_free(str_a); 717 g_free(str_b); 718 719 return val; 720 } 721 722 static gboolean tree_view_folder_item_func(GtkTreeModel *model, 723 GtkTreePath *path, 724 GtkTreeIter *iter, 725 FolderItemSearch *data) 726 { 727 FolderItem *item = NULL; 728 729 gtk_tree_model_get(model, iter, FOLDERSEL_FOLDERITEM, &item, -1); 730 731 if (data->item == item) { 732 data->path = gtk_tree_path_copy(path); 733 data->iter = *iter; 734 return TRUE; 735 } 736 737 return FALSE; 738 }