mimeview.c (60509B)
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 <sys/types.h> 27 #include <sys/stat.h> 28 29 #include "main.h" 30 #include "mimeview.h" 31 #include "textview.h" 32 #include "procmime.h" 33 #include "summaryview.h" 34 #include "menu.h" 35 #include "filesel.h" 36 #include "alertpanel.h" 37 #include "inputdialog.h" 38 #include "utils.h" 39 #include "gtkutils.h" 40 #include "prefs_common.h" 41 #include "procheader.h" 42 #include "stock_pixmap.h" 43 #include "gtk/gtkvscrollbutton.h" 44 #include "gtk/logwindow.h" 45 #include "manage_window.h" 46 #include "file-utils.h" 47 48 typedef enum 49 { 50 COL_MIMETYPE = 0, 51 COL_SIZE = 1, 52 COL_NAME = 2, 53 COL_DATA = 3, 54 N_MIMEVIEW_COLUMNS 55 } MimeViewColumnPos; 56 57 #define N_MIMEVIEW_COLS (N_MIMEVIEW_COLUMNS) 58 59 static void mimeview_set_multipart_tree (MimeView *mimeview, 60 MimeInfo *mimeinfo, 61 GtkTreeIter *parent); 62 static void mimeview_show_message_part (MimeView *mimeview, 63 MimeInfo *partinfo); 64 static void mimeview_change_view_type (MimeView *mimeview, 65 MimeViewType type); 66 static gchar *mimeview_get_filename_for_part (MimeInfo *partinfo, 67 const gchar *basedir, 68 gint number); 69 static gint mimeview_write_part (const gchar *filename, 70 MimeInfo *partinfo, 71 gboolean handle_error); 72 73 static void mimeview_selected (GtkTreeSelection *selection, 74 MimeView *mimeview); 75 static gint mimeview_button_pressed (GtkWidget *widget, 76 GdkEventButton *event, 77 MimeView *mimeview); 78 79 static gboolean mimeview_scrolled (GtkWidget *widget, 80 GdkEventScroll *event, 81 MimeView *mimeview); 82 83 static void mimeview_save_all (MimeView *mimeview, 84 gboolean attachments_only); 85 static void mimeview_open_part_with (MimeView *mimeview, 86 MimeInfo *partinfo, 87 gboolean automatic); 88 static void mimeview_send_to (MimeView *mimeview, 89 MimeInfo *partinfo); 90 static void mimeview_view_file (const gchar *filename, 91 MimeInfo *partinfo, 92 const gchar *cmd, 93 MimeView *mimeview); 94 static gboolean icon_clicked_cb (GtkWidget *button, 95 GdkEventButton *event, 96 MimeView *mimeview); 97 static void icon_selected (MimeView *mimeview, 98 gint num, 99 MimeInfo *partinfo); 100 static gint icon_key_pressed (GtkWidget *button, 101 GdkEventKey *event, 102 MimeView *mimeview); 103 static void icon_list_append_icon (MimeView *mimeview, 104 MimeInfo *mimeinfo); 105 static void icon_list_create (MimeView *mimeview, 106 MimeInfo *mimeinfo); 107 static void icon_list_clear (MimeView *mimeview); 108 static void icon_list_toggle_by_mime_info (MimeView *mimeview, 109 MimeInfo *mimeinfo); 110 static void ctree_size_allocate_cb (GtkWidget *widget, 111 GtkAllocation *allocation, 112 MimeView *mimeview); 113 static gint mime_toggle_button_cb(GtkWidget *button, GdkEventButton *event, 114 MimeView *mimeview); 115 static gboolean part_button_pressed (MimeView *mimeview, 116 GdkEventButton *event, 117 MimeInfo *partinfo); 118 static void icon_scroll_size_allocate_cb(GtkWidget *widget, 119 GtkAllocation *layout_size, 120 MimeView *mimeview); 121 static MimeInfo *mimeview_get_part_to_use(MimeView *mimeview); 122 static const gchar *get_part_name(MimeInfo *partinfo); 123 static const gchar *get_part_description(MimeInfo *partinfo); 124 125 static void mimeview_launch_cb(GtkAction *action, gpointer data) 126 { 127 MimeView *mimeview = (MimeView *)data; 128 mimeview_launch(mimeview, mimeview_get_part_to_use(mimeview)); 129 } 130 131 static void mimeview_open_with_cb(GtkAction *action, gpointer data) 132 { 133 mimeview_open_with((MimeView *)data); 134 } 135 136 static void mimeview_copy_cb(GtkAction *action, gpointer data) 137 { 138 MimeView *mimeview = (MimeView *)data; 139 MimeInfo *mimeinfo = mimeview_get_part_to_use(mimeview); 140 141 if (mimeinfo == NULL) 142 return; 143 144 if (mimeinfo->type == MIMETYPE_IMAGE) { 145 GError *error = NULL; 146 GdkPixbuf *pixbuf = procmime_get_part_as_pixbuf(mimeinfo, &error); 147 if (error != NULL) { 148 g_warning("could not copy: %s", error->message); 149 g_error_free(error); 150 } else { 151 gtk_clipboard_set_image(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), 152 pixbuf); 153 } 154 g_object_unref(pixbuf); 155 } else { 156 void *data = procmime_get_part_as_string(mimeinfo, FALSE); 157 gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), 158 data, (gint)mimeinfo->length); 159 g_free(data); 160 } 161 } 162 163 static void mimeview_send_to_cb(GtkAction *action, gpointer data) 164 { 165 MimeView *mimeview = (MimeView *)data; 166 mimeview_send_to(mimeview, mimeview_get_part_to_use(mimeview)); 167 } 168 169 static void mimeview_display_as_text_cb(GtkAction *action, gpointer data) 170 { 171 mimeview_display_as_text((MimeView *)data); 172 } 173 174 static void mimeview_save_as_cb(GtkAction *action, gpointer data) 175 { 176 mimeview_save_as((MimeView *)data); 177 } 178 179 static void mimeview_save_all_cb(GtkAction *action, gpointer data) 180 { 181 mimeview_save_all((MimeView *)data, FALSE); 182 } 183 184 static void mimeview_save_all_attachments_cb(GtkAction *action, gpointer data) 185 { 186 mimeview_save_all((MimeView *)data, TRUE); 187 } 188 189 static void mimeview_select_next_part_cb(GtkAction *action, gpointer data) 190 { 191 mimeview_select_next_part((MimeView *)data); 192 } 193 194 static void mimeview_select_prev_part_cb(GtkAction *action, gpointer data) 195 { 196 mimeview_select_prev_part((MimeView *)data); 197 } 198 199 static GtkActionEntry mimeview_menu_actions[] = { 200 { "MimeView", NULL, "MimeView", NULL, NULL, NULL }, 201 { "MimeView/Open", NULL, N_("_Open"), NULL, "Open MIME part", G_CALLBACK(mimeview_launch_cb) }, 202 { "MimeView/OpenWith", NULL, N_("Open _with..."), NULL, "Open MIME part with...", G_CALLBACK(mimeview_open_with_cb) }, 203 { "MimeView/Copy", NULL, N_("Copy"), NULL, "Copy", G_CALLBACK(mimeview_copy_cb) }, 204 { "MimeView/SendTo", NULL, N_("Send to..."), NULL, "Send to", G_CALLBACK(mimeview_send_to_cb) }, 205 { "MimeView/DisplayAsText", NULL, N_("_Display as text"), NULL, "Display as text", G_CALLBACK(mimeview_display_as_text_cb) }, 206 { "MimeView/SaveAs", NULL, N_("_Save as..."), NULL, "Save as", G_CALLBACK(mimeview_save_as_cb) }, 207 { "MimeView/SaveAll", NULL, N_("Save _all..."), NULL, "Save all parts", G_CALLBACK(mimeview_save_all_cb) }, 208 { "MimeView/SaveAllAttachments", NULL, N_("Save all attachments..."), NULL, "Save all attachments", G_CALLBACK(mimeview_save_all_attachments_cb) }, 209 { "MimeView/NextPart", NULL, N_("Next part"), NULL, "Next part", G_CALLBACK(mimeview_select_next_part_cb) }, 210 { "MimeView/PrevPart", NULL, N_("Previous part"), NULL, "Previous part", G_CALLBACK(mimeview_select_prev_part_cb) } 211 }; 212 213 static GtkTargetEntry mimeview_mime_types[] = 214 { 215 {"text/uri-list", 0, 0} 216 }; 217 218 GSList *mimeviewer_factories; 219 GSList *mimeviews; 220 221 MimeView *mimeview_create(MainWindow *mainwin) 222 { 223 MimeView *mimeview; 224 225 GtkWidget *paned; 226 GtkWidget *scrolledwin; 227 GtkWidget *ctree; 228 GtkWidget *mime_notebook; 229 GtkWidget *popupmenu; 230 GtkWidget *ctree_mainbox; 231 GtkWidget *vbox; 232 GtkWidget *mime_toggle; 233 GtkWidget *icon_mainbox; 234 GtkWidget *icon_scroll; 235 GtkWidget *icon_grid; 236 GtkWidget *arrow; 237 GtkWidget *scrollbutton; 238 GtkWidget *hbox; 239 GtkTreeStore *model; 240 GtkCellRenderer *renderer; 241 GtkTreeViewColumn *column; 242 GtkTreeSelection *selection; 243 244 gchar *titles[N_MIMEVIEW_COLS]; 245 gint cols; 246 247 mimeview = g_new0(MimeView, 1); 248 249 titles[COL_MIMETYPE] = _("MIME Type"); 250 titles[COL_SIZE] = _("Size"); 251 titles[COL_NAME] = _("Name"); 252 253 scrolledwin = gtk_scrolled_window_new(NULL, NULL); 254 gtk_widget_show(scrolledwin); 255 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), 256 GTK_POLICY_AUTOMATIC, 257 GTK_POLICY_AUTOMATIC); 258 259 model = gtk_tree_store_new(N_MIMEVIEW_COLS, 260 G_TYPE_STRING, 261 G_TYPE_STRING, 262 G_TYPE_STRING, 263 G_TYPE_POINTER); 264 265 ctree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); 266 g_object_unref(model); 267 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(ctree), FALSE); 268 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(ctree), 269 prefs_common.show_col_headers); 270 271 renderer = gtk_cell_renderer_text_new(); 272 cols = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(ctree), 273 -1, titles[COL_MIMETYPE],renderer, 274 "text", COL_MIMETYPE, NULL); 275 column = gtk_tree_view_get_column(GTK_TREE_VIEW(ctree), cols-1); 276 277 renderer = gtk_cell_renderer_text_new(); 278 gtk_cell_renderer_set_alignment(renderer, 1, 0.5); 279 cols = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(ctree), 280 -1, titles[COL_SIZE], renderer, 281 "text", COL_SIZE, NULL); 282 column = gtk_tree_view_get_column(GTK_TREE_VIEW(ctree), cols-1); 283 gtk_tree_view_column_set_alignment(GTK_TREE_VIEW_COLUMN(column), 1); 284 285 renderer = gtk_cell_renderer_text_new(); 286 cols = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(ctree), 287 -1, titles[COL_NAME], renderer, 288 "text", COL_NAME, NULL); 289 column = gtk_tree_view_get_column(GTK_TREE_VIEW(ctree), cols-1); 290 gtk_tree_view_column_set_expand(GTK_TREE_VIEW_COLUMN(column), TRUE); 291 292 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ctree)); 293 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE); 294 295 gtk_widget_show(ctree); 296 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree); 297 298 g_signal_connect(G_OBJECT(selection), "changed", 299 G_CALLBACK(mimeview_selected), mimeview); 300 g_signal_connect(G_OBJECT(ctree), "button_release_event", 301 G_CALLBACK(mimeview_button_pressed), mimeview); 302 303 mime_notebook = gtk_notebook_new(); 304 gtk_widget_set_name(GTK_WIDGET(mime_notebook), "mime_notebook"); 305 gtk_widget_show(mime_notebook); 306 gtk_widget_set_can_focus(mime_notebook, FALSE); 307 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(mime_notebook), FALSE); 308 gtk_notebook_set_show_border(GTK_NOTEBOOK(mime_notebook), FALSE); 309 310 icon_grid = gtk_grid_new(); 311 gtk_orientable_set_orientation(GTK_ORIENTABLE(icon_grid), 312 GTK_ORIENTATION_VERTICAL); 313 gtk_grid_set_row_spacing(GTK_GRID(icon_grid), 0); 314 gtk_widget_show(icon_grid); 315 icon_scroll = gtk_layout_new(NULL, NULL); 316 gtk_widget_show(icon_scroll); 317 gtk_layout_put(GTK_LAYOUT(icon_scroll), icon_grid, 0, 0); 318 scrollbutton = gtk_vscrollbutton_new(gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(icon_scroll))); 319 gtk_widget_show(scrollbutton); 320 321 g_signal_connect(G_OBJECT(icon_scroll), "scroll_event", 322 G_CALLBACK(mimeview_scrolled), mimeview); 323 324 mime_toggle = gtk_event_box_new(); 325 gtk_event_box_set_visible_window(GTK_EVENT_BOX(mime_toggle), FALSE); 326 327 gtk_container_set_border_width(GTK_CONTAINER(mime_toggle), 2); 328 gtk_widget_show(mime_toggle); 329 mimeview->ctree_mode = FALSE; 330 arrow = gtk_image_new_from_icon_name("pan-start-symbolic", GTK_ICON_SIZE_MENU); 331 gtk_widget_show(arrow); 332 gtk_container_add(GTK_CONTAINER(mime_toggle), arrow); 333 g_signal_connect(G_OBJECT(mime_toggle), "button_release_event", 334 G_CALLBACK(mime_toggle_button_cb), mimeview); 335 336 icon_mainbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); 337 gtk_widget_set_name(GTK_WIDGET(icon_mainbox), "mimeview_icon_mainbox"); 338 gtk_widget_show(icon_mainbox); 339 gtk_widget_set_size_request(icon_mainbox, 32, -1); 340 gtk_box_pack_start(GTK_BOX(icon_mainbox), mime_toggle, FALSE, FALSE, 0); 341 gtk_box_pack_start(GTK_BOX(icon_mainbox), icon_scroll, TRUE, TRUE, 3); 342 gtk_box_pack_end(GTK_BOX(icon_mainbox), scrollbutton, FALSE, FALSE, 0); 343 g_signal_connect(G_OBJECT(icon_mainbox), "size_allocate", 344 G_CALLBACK(icon_scroll_size_allocate_cb), mimeview); 345 346 ctree_mainbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); 347 gtk_widget_set_name(GTK_WIDGET(ctree_mainbox), "mimeview_ctree_mainbox"); 348 gtk_box_pack_start(GTK_BOX(ctree_mainbox), scrolledwin, TRUE, TRUE, 0); 349 g_signal_connect(G_OBJECT(ctree_mainbox), "size_allocate", 350 G_CALLBACK(ctree_size_allocate_cb), mimeview); 351 352 mimeview->ui_manager = gtk_ui_manager_new(); 353 mimeview->action_group = cm_menu_create_action_group_full(mimeview->ui_manager, 354 "MimeView", mimeview_menu_actions, 355 G_N_ELEMENTS(mimeview_menu_actions), (gpointer)mimeview); 356 357 MENUITEM_ADDUI_MANAGER(mimeview->ui_manager, "/", "Menus", "Menus", GTK_UI_MANAGER_MENUBAR) 358 MENUITEM_ADDUI_MANAGER(mimeview->ui_manager, 359 "/Menus/", "MimeView", "MimeView", GTK_UI_MANAGER_MENU); 360 MENUITEM_ADDUI_MANAGER(mimeview->ui_manager, 361 "/Menus/MimeView/", "Open", "MimeView/Open", 362 GTK_UI_MANAGER_MENUITEM); 363 MENUITEM_ADDUI_MANAGER(mimeview->ui_manager, 364 "/Menus/MimeView/", "OpenWith", "MimeView/OpenWith", 365 GTK_UI_MANAGER_MENUITEM); 366 MENUITEM_ADDUI_MANAGER(mimeview->ui_manager, 367 "/Menus/MimeView/", "Copy", "MimeView/Copy", 368 GTK_UI_MANAGER_MENUITEM); 369 MENUITEM_ADDUI_MANAGER(mimeview->ui_manager, 370 "/Menus/MimeView/", "SendTo", "MimeView/SendTo", 371 GTK_UI_MANAGER_MENUITEM); 372 MENUITEM_ADDUI_MANAGER(mimeview->ui_manager, 373 "/Menus/MimeView/", "DisplayAsText", "MimeView/DisplayAsText", 374 GTK_UI_MANAGER_MENUITEM); 375 MENUITEM_ADDUI_MANAGER(mimeview->ui_manager, 376 "/Menus/MimeView/", "SaveAs", "MimeView/SaveAs", 377 GTK_UI_MANAGER_MENUITEM); 378 MENUITEM_ADDUI_MANAGER(mimeview->ui_manager, 379 "/Menus/MimeView/", "SaveAll", "MimeView/SaveAll", 380 GTK_UI_MANAGER_MENUITEM); 381 MENUITEM_ADDUI_MANAGER(mimeview->ui_manager, 382 "/Menus/MimeView/", "SaveAllAttachments", "MimeView/SaveAllAttachments", 383 GTK_UI_MANAGER_MENUITEM); 384 MENUITEM_ADDUI_MANAGER(mimeview->ui_manager, 385 "/Menus/MimeView/", "NextPart", "MimeView/NextPart", 386 GTK_UI_MANAGER_MENUITEM); 387 MENUITEM_ADDUI_MANAGER(mimeview->ui_manager, 388 "/Menus/MimeView/", "PrevPart", "MimeView/PrevPart", 389 GTK_UI_MANAGER_MENUITEM); 390 391 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM( 392 gtk_ui_manager_get_widget(mimeview->ui_manager, "/Menus/MimeView")) ); 393 394 395 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); 396 gtk_widget_show(vbox); 397 gtk_box_pack_start(GTK_BOX(vbox), mime_notebook, TRUE, TRUE, 0); 398 399 paned = gtk_paned_new(GTK_ORIENTATION_VERTICAL); 400 gtk_widget_show(paned); 401 gtk_paned_pack1(GTK_PANED(paned), ctree_mainbox, FALSE, TRUE); 402 gtk_paned_pack2(GTK_PANED(paned), vbox, TRUE, TRUE); 403 404 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); 405 gtk_widget_set_name(GTK_WIDGET(hbox), "mimeview"); 406 gtk_box_pack_start(GTK_BOX(hbox), paned, TRUE, TRUE, 0); 407 gtk_box_pack_start(GTK_BOX(hbox), icon_mainbox, FALSE, FALSE, 0); 408 409 gtk_widget_show(hbox); 410 gtk_widget_hide(ctree_mainbox); 411 mimeview->hbox = hbox; 412 mimeview->paned = paned; 413 mimeview->scrolledwin = scrolledwin; 414 mimeview->ctree = ctree; 415 mimeview->mime_notebook = mime_notebook; 416 mimeview->popupmenu = popupmenu; 417 mimeview->type = -1; 418 mimeview->ctree_mainbox = ctree_mainbox; 419 mimeview->icon_scroll = icon_scroll; 420 mimeview->icon_grid = icon_grid; 421 mimeview->icon_mainbox = icon_mainbox; 422 mimeview->icon_count = 0; 423 mimeview->mainwin = mainwin; 424 mimeview->mime_toggle = mime_toggle; 425 mimeview->scrollbutton = scrollbutton; 426 mimeview->arrow = arrow; 427 mimeview->target_list = gtk_target_list_new(mimeview_mime_types, 1); 428 429 mimeviews = g_slist_prepend(mimeviews, mimeview); 430 431 return mimeview; 432 } 433 434 void mimeview_init(MimeView *mimeview) 435 { 436 textview_init(mimeview->textview); 437 438 gtk_container_add(GTK_CONTAINER(mimeview->mime_notebook), 439 GTK_WIDGET_PTR(mimeview->textview)); 440 } 441 442 void mimeview_show_message(MimeView *mimeview, MimeInfo *mimeinfo, 443 const gchar *file) 444 { 445 GtkTreeView *ctree = GTK_TREE_VIEW(mimeview->ctree); 446 447 mimeview_clear(mimeview); 448 449 mimeview->mimeinfo = mimeinfo; 450 mimeview->file = strdup(file); 451 452 g_signal_handlers_block_by_func(G_OBJECT(ctree), mimeview_selected, mimeview); 453 454 mimeview_set_multipart_tree(mimeview, mimeinfo, NULL); 455 gtk_tree_view_expand_all(ctree); 456 icon_list_create(mimeview, mimeinfo); 457 458 g_signal_handlers_unblock_by_func(G_OBJECT(ctree), mimeview_selected, mimeview); 459 } 460 461 static void mimeview_free_mimeinfo(MimeView *mimeview) 462 { 463 if (mimeview->mimeinfo != NULL) { 464 procmime_mimeinfo_free_all(&mimeview->mimeinfo); 465 mimeview->mimeinfo = NULL; 466 } 467 } 468 469 void mimeview_destroy(MimeView *mimeview) 470 { 471 GSList *cur; 472 473 for (cur = mimeview->viewers; cur != NULL; cur = g_slist_next(cur)) { 474 MimeViewer *viewer = (MimeViewer *) cur->data; 475 viewer->destroy_viewer(viewer); 476 } 477 g_slist_free(mimeview->viewers); 478 gtk_target_list_unref(mimeview->target_list); 479 480 mimeview_free_mimeinfo(mimeview); 481 gtk_tree_path_free(mimeview->opened); 482 g_free(mimeview->file); 483 mimeviews = g_slist_remove(mimeviews, mimeview); 484 g_free(mimeview); 485 } 486 487 MimeInfo *mimeview_get_selected_part(MimeView *mimeview) 488 { 489 return gtkut_tree_view_get_selected_pointer( 490 GTK_TREE_VIEW(mimeview->ctree), COL_DATA, 491 NULL, NULL, NULL); 492 } 493 494 MimeInfo *mimeview_get_node_part(MimeView *mimeview, GtkTreePath *path) 495 { 496 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(mimeview->ctree)); 497 GtkTreeIter iter; 498 MimeInfo *partinfo; 499 500 gtk_tree_model_get_iter(model, &iter, path); 501 gtk_tree_model_get(model, &iter, COL_DATA, &partinfo, -1); 502 return partinfo; 503 } 504 505 gboolean mimeview_tree_is_empty(MimeView *mimeview) 506 { 507 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(mimeview->ctree)); 508 GtkTreeIter iter; 509 return !gtk_tree_model_get_iter_first(model, &iter); 510 } 511 512 static gboolean mimeview_tree_next(GtkTreeModel *model, GtkTreePath *path) 513 { 514 GtkTreeIter iter, parent; 515 gboolean has_parent; 516 517 gtk_tree_model_get_iter(model, &iter, path); 518 519 if (gtk_tree_model_iter_has_child(model, &iter)) { 520 gtk_tree_path_down(path); 521 return TRUE; 522 } 523 524 has_parent = gtk_tree_model_iter_parent(model, &parent, &iter); 525 526 if (!gtk_tree_model_iter_next(model, &iter)) { 527 while (has_parent) { 528 GtkTreeIter saved_parent = parent; 529 gtk_tree_path_up(path); 530 if (gtk_tree_model_iter_next(model, &parent)) { 531 gtk_tree_path_next(path); 532 return TRUE; 533 } else { 534 has_parent = gtk_tree_model_iter_parent(model, &parent, &saved_parent); 535 } 536 } 537 538 } else { 539 gtk_tree_path_next(path); 540 return TRUE; 541 } 542 543 return FALSE; 544 } 545 546 static gboolean mimeview_tree_prev(MimeView *mimeview, GtkTreePath *path) 547 { 548 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(mimeview->ctree)); 549 GtkTreeIter iter, child, parent; 550 gboolean has_parent; 551 552 gtk_tree_model_get_iter(model, &iter, path); 553 has_parent = gtk_tree_model_iter_parent(model, &parent, &iter); 554 555 if (gtk_tree_path_prev(path)) { 556 gtk_tree_model_get_iter(model, &iter, path); 557 558 if (gtk_tree_model_iter_nth_child(model, &child, &iter, 0)) { 559 gtk_tree_path_down(path); 560 561 while (gtk_tree_model_iter_next(model, &child)) 562 gtk_tree_path_next(path); 563 } 564 565 return TRUE; 566 } 567 568 if (has_parent) { 569 gtk_tree_path_up(path); 570 return TRUE; 571 } 572 573 return FALSE; 574 } 575 576 gint mimeview_get_selected_part_num(MimeView *mimeview) 577 { 578 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(mimeview->ctree)); 579 GtkTreeIter iter; 580 GtkTreePath *path; 581 gint i = 0; 582 583 if (!gtk_tree_model_get_iter_first(model, &iter)) 584 return -1; 585 path = gtk_tree_model_get_path(model, &iter); 586 587 do { 588 if (!gtk_tree_path_compare(mimeview->opened, path)) { 589 gtk_tree_path_free(path); 590 return i; 591 } 592 593 i++; 594 } while (mimeview_tree_next(model, path)); 595 596 gtk_tree_path_free(path); 597 598 return -1; 599 } 600 601 void mimeview_select_part_num(MimeView *mimeview, gint i) 602 { 603 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(mimeview->ctree)); 604 GtkTreeIter iter; 605 GtkTreePath *path; 606 GtkTreeSelection *selection; 607 gint x = 0; 608 609 if (i < 0) 610 return; 611 612 if (!gtk_tree_model_get_iter_first(model, &iter)) 613 return; 614 path = gtk_tree_model_get_path(model, &iter); 615 616 while (x != i) { 617 if (!mimeview_tree_next(model, path)) { 618 gtk_tree_path_free(path); 619 return; 620 } 621 x++; 622 } 623 624 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(mimeview->ctree)); 625 gtk_tree_selection_select_path(selection, path); 626 gtk_tree_path_free(path); 627 } 628 629 static void mimeview_set_multipart_tree(MimeView *mimeview, 630 MimeInfo *mimeinfo, 631 GtkTreeIter *parent) 632 { 633 GtkTreeStore *model = GTK_TREE_STORE(gtk_tree_view_get_model( 634 GTK_TREE_VIEW(mimeview->ctree))); 635 GtkTreeIter iter; 636 gchar *content_type, *length, *name; 637 638 cm_return_if_fail(mimeinfo != NULL); 639 640 while (mimeinfo != NULL) { 641 if (mimeinfo->type != MIMETYPE_UNKNOWN && mimeinfo->subtype) 642 content_type = g_strdup_printf("%s/%s", 643 procmime_get_media_type_str(mimeinfo->type), 644 mimeinfo->subtype); 645 else 646 content_type = g_strdup("UNKNOWN"); 647 648 length = g_strdup(to_human_readable((goffset) mimeinfo->length)); 649 650 if (prefs_common.attach_desc) 651 name = g_strdup(get_part_description(mimeinfo)); 652 else 653 name = g_strdup(get_part_name(mimeinfo)); 654 655 gtk_tree_store_append(model, &iter, parent); 656 gtk_tree_store_set(model, &iter, 657 COL_MIMETYPE, content_type, 658 COL_SIZE, length, 659 COL_NAME, name, 660 COL_DATA, mimeinfo, -1); 661 g_free(content_type); 662 g_free(length); 663 g_free(name); 664 665 if (mimeinfo->node->children) 666 mimeview_set_multipart_tree(mimeview, 667 (MimeInfo *) mimeinfo->node->children->data, &iter); 668 mimeinfo = mimeinfo->node->next != NULL ? 669 (MimeInfo *) mimeinfo->node->next->data : NULL; 670 } 671 } 672 673 static const gchar *get_real_part_name(MimeInfo *partinfo) 674 { 675 const gchar *name = NULL; 676 677 name = procmime_mimeinfo_get_parameter(partinfo, "filename"); 678 if (name == NULL) 679 name = procmime_mimeinfo_get_parameter(partinfo, "name"); 680 681 return name; 682 } 683 684 static const gchar *get_part_name(MimeInfo *partinfo) 685 { 686 const gchar *name; 687 688 name = get_real_part_name(partinfo); 689 if (name == NULL) 690 name = ""; 691 692 return name; 693 } 694 695 static const gchar *get_part_description(MimeInfo *partinfo) 696 { 697 if (partinfo->description) 698 return partinfo->description; 699 else 700 return get_part_name(partinfo); 701 } 702 703 static void mimeview_show_message_part(MimeView *mimeview, MimeInfo *partinfo) 704 { 705 if (!partinfo) return; 706 707 FILE *fp; 708 if ((fp = fopen(mimeview->file, "r")) == NULL) { 709 warn("open %s", mimeview->file); 710 return; 711 } 712 713 mimeview_change_view_type(mimeview, MIMEVIEW_TEXT); 714 textview_show_part(mimeview->textview, partinfo, fp); 715 716 fclose(fp); 717 } 718 719 static MimeViewer *get_viewer_for_content_type(MimeView *mimeview, const gchar *content_type) 720 { 721 GSList *cur; 722 MimeViewerFactory *factory = NULL; 723 MimeViewer *viewer = NULL; 724 gchar *real_contenttype = NULL, *tmp; 725 726 real_contenttype = g_utf8_strdown((gchar *)content_type, -1); 727 728 for (cur = mimeviewer_factories; cur != NULL; cur = g_slist_next(cur)) { 729 MimeViewerFactory *curfactory = cur->data; 730 gint i = 0; 731 732 while (curfactory->content_types[i] != NULL) { 733 tmp = g_utf8_strdown(curfactory->content_types[i], -1); 734 if (g_pattern_match_simple(tmp, real_contenttype)) { 735 debug_print("%s\n", curfactory->content_types[i]); 736 factory = curfactory; 737 g_free(tmp); 738 break; 739 } 740 g_free(tmp); 741 i++; 742 } 743 if (factory != NULL) 744 break; 745 } 746 g_free(real_contenttype); 747 if (factory == NULL) 748 return NULL; 749 750 for (cur = mimeview->viewers; cur != NULL; cur = g_slist_next(cur)) { 751 MimeViewer *curviewer = cur->data; 752 753 if (curviewer->factory == factory) 754 return curviewer; 755 } 756 viewer = factory->create_viewer(); 757 gtk_container_add(GTK_CONTAINER(mimeview->mime_notebook), 758 GTK_WIDGET(viewer->get_widget(viewer))); 759 760 mimeview->viewers = g_slist_append(mimeview->viewers, viewer); 761 762 return viewer; 763 } 764 765 gboolean mimeview_has_viewer_for_content_type(MimeView *mimeview, const gchar *content_type) 766 { 767 return (get_viewer_for_content_type(mimeview, content_type) != NULL); 768 } 769 770 static MimeViewer *get_viewer_for_mimeinfo(MimeView *mimeview, MimeInfo *partinfo) 771 { 772 gchar *content_type = NULL; 773 MimeViewer *viewer = NULL; 774 775 if ((partinfo->type == MIMETYPE_APPLICATION) && 776 (!g_ascii_strcasecmp(partinfo->subtype, "octet-stream"))) { 777 const gchar *filename; 778 779 filename = procmime_mimeinfo_get_parameter(partinfo, "filename"); 780 if (filename == NULL) 781 filename = procmime_mimeinfo_get_parameter(partinfo, "name"); 782 if (filename != NULL) 783 content_type = procmime_get_mime_type(filename); 784 } else { 785 content_type = procmime_get_content_type_str(partinfo->type, partinfo->subtype); 786 } 787 788 if (content_type != NULL) { 789 viewer = get_viewer_for_content_type(mimeview, content_type); 790 g_free(content_type); 791 } 792 793 return viewer; 794 } 795 796 gboolean mimeview_show_part(MimeView *mimeview, MimeInfo *partinfo) 797 { 798 MimeViewer *viewer; 799 800 viewer = get_viewer_for_mimeinfo(mimeview, partinfo); 801 if (viewer == NULL) { 802 if (mimeview->mimeviewer != NULL) 803 mimeview->mimeviewer->clear_viewer(mimeview->mimeviewer); 804 mimeview->mimeviewer = NULL; 805 return FALSE; 806 } 807 808 if (mimeview->mimeviewer != NULL) 809 mimeview->mimeviewer->clear_viewer(mimeview->mimeviewer); 810 811 if (mimeview->mimeviewer != viewer) 812 mimeview->mimeviewer = viewer; 813 814 mimeview_change_view_type(mimeview, MIMEVIEW_VIEWER); 815 viewer->mimeview = mimeview; 816 viewer->show_mimepart(viewer, mimeview->file, partinfo); 817 818 return TRUE; 819 } 820 821 static void mimeview_change_view_type(MimeView *mimeview, MimeViewType type) 822 { 823 TextView *textview = mimeview->textview; 824 GtkWidget *focused = NULL; 825 826 if (mainwindow_get_mainwindow()) 827 focused = gtkut_get_focused_child( 828 GTK_CONTAINER(mainwindow_get_mainwindow()->window)); 829 830 if ((mimeview->type != MIMEVIEW_VIEWER) && 831 (mimeview->type == type)) return; 832 833 switch (type) { 834 case MIMEVIEW_TEXT: 835 gtk_notebook_set_current_page(GTK_NOTEBOOK(mimeview->mime_notebook), 836 gtk_notebook_page_num(GTK_NOTEBOOK(mimeview->mime_notebook), 837 GTK_WIDGET_PTR(textview))); 838 break; 839 case MIMEVIEW_VIEWER: 840 gtk_notebook_set_current_page(GTK_NOTEBOOK(mimeview->mime_notebook), 841 gtk_notebook_page_num(GTK_NOTEBOOK(mimeview->mime_notebook), 842 GTK_WIDGET(mimeview->mimeviewer->get_widget(mimeview->mimeviewer)))); 843 break; 844 default: 845 return; 846 } 847 if (focused) 848 gtk_widget_grab_focus(focused); 849 mimeview->type = type; 850 } 851 852 void mimeview_clear(MimeView *mimeview) 853 { 854 GtkTreeModel *model; 855 856 if (!mimeview) 857 return; 858 if (g_slist_find(mimeviews, mimeview) == NULL) 859 return; 860 861 model = gtk_tree_view_get_model(GTK_TREE_VIEW(mimeview->ctree)); 862 gtk_tree_store_clear(GTK_TREE_STORE(model)); 863 864 textview_clear(mimeview->textview); 865 if (mimeview->mimeviewer != NULL) 866 mimeview->mimeviewer->clear_viewer(mimeview->mimeviewer); 867 868 mimeview_free_mimeinfo(mimeview); 869 870 mimeview->mimeinfo = NULL; 871 872 gtk_tree_path_free(mimeview->opened); 873 mimeview->opened = NULL; 874 875 g_free(mimeview->file); 876 mimeview->file = NULL; 877 878 icon_list_clear(mimeview); 879 mimeview_change_view_type(mimeview, MIMEVIEW_TEXT); 880 } 881 882 static void mimeview_selected(GtkTreeSelection *selection, MimeView *mimeview) 883 { 884 GtkTreeView *ctree = GTK_TREE_VIEW(mimeview->ctree); 885 GtkTreeModel *model; 886 GtkTreeIter iter; 887 GtkTreePath *path; 888 MimeInfo *partinfo; 889 MainWindow *mainwin; 890 GdkDisplay *display; 891 GdkSeat *seat; 892 GdkDevice *device; 893 894 selection = gtk_tree_view_get_selection(ctree); 895 if (!gtk_tree_selection_get_selected(selection, &model, &iter)) 896 return; 897 898 path = gtk_tree_model_get_path(model, &iter); 899 900 if (mimeview->opened && !gtk_tree_path_compare(mimeview->opened, path)) { 901 gtk_tree_path_free(path); 902 return; 903 } 904 905 gtk_tree_path_free(mimeview->opened); 906 mimeview->opened = path; 907 mimeview->spec_part = NULL; 908 gtk_tree_view_scroll_to_cell(ctree, path, NULL, TRUE, 0.5, 0); 909 910 partinfo = mimeview_get_node_part(mimeview, path); 911 if (!partinfo) 912 return; 913 914 /* ungrab the mouse event */ 915 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) { 916 gtk_grab_remove(GTK_WIDGET(ctree)); 917 display = gdk_window_get_display(gtk_widget_get_window(GTK_WIDGET(ctree))); 918 seat = gdk_display_get_default_seat(display); 919 device = gdk_seat_get_pointer(seat); 920 if (gdk_display_device_is_grabbed(display, device)) 921 gdk_seat_ungrab(seat); 922 } 923 924 mimeview->textview->default_text = FALSE; 925 926 if (!mimeview_show_part(mimeview, partinfo)) { 927 switch (partinfo->type) { 928 case MIMETYPE_TEXT: 929 case MIMETYPE_MESSAGE: 930 case MIMETYPE_MULTIPART: 931 mimeview_show_message_part(mimeview, partinfo); 932 break; 933 default: 934 mimeview->textview->default_text = TRUE; 935 mimeview_change_view_type(mimeview, MIMEVIEW_TEXT); 936 textview_clear(mimeview->textview); 937 textview_show_mime_part(mimeview->textview, partinfo); 938 break; 939 } 940 } 941 mainwin = mainwindow_get_mainwindow(); 942 if (mainwin) 943 main_window_set_menu_sensitive(mainwin); 944 } 945 946 static gint mimeview_button_pressed(GtkWidget *widget, GdkEventButton *event, 947 MimeView *mimeview) 948 { 949 GtkTreeSelection *selection; 950 GtkTreeIter iter; 951 952 if (!event) return FALSE; 953 954 if (event->button == 2 || event->button == 3) { 955 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); 956 if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) 957 return FALSE; 958 959 gtk_tree_selection_select_iter(selection, &iter); 960 } 961 part_button_pressed(mimeview, event, mimeview_get_selected_part(mimeview)); 962 963 return FALSE; 964 } 965 966 static gboolean mimeview_scrolled(GtkWidget *widget, GdkEventScroll *event, 967 MimeView *mimeview) 968 { 969 GtkVScrollbutton *scrollbutton = (GtkVScrollbutton *)mimeview->scrollbutton; 970 if (event->direction == GDK_SCROLL_UP) { 971 scrollbutton->scroll_type = GTK_SCROLL_STEP_BACKWARD; 972 } else if (event->direction == GDK_SCROLL_DOWN) { 973 scrollbutton->scroll_type = GTK_SCROLL_STEP_FORWARD; 974 } else { 975 gdouble x, y; 976 977 if ((event->direction == GDK_SCROLL_SMOOTH) && 978 gdk_event_get_scroll_deltas((GdkEvent*)event, &x, &y)) { 979 if (y < 0) 980 scrollbutton->scroll_type = GTK_SCROLL_STEP_BACKWARD; 981 else 982 if (y >0) 983 scrollbutton->scroll_type = GTK_SCROLL_STEP_FORWARD; 984 } else 985 return FALSE; /* Scrolling left or right */ 986 } 987 gtk_vscrollbutton_scroll(scrollbutton); 988 return TRUE; 989 } 990 991 static gboolean part_button_pressed(MimeView *mimeview, GdkEventButton *event, 992 MimeInfo *partinfo) 993 { 994 static MimeInfo *lastinfo; 995 static guint32 lasttime; 996 997 gint double_click_time; 998 g_object_get(gtk_settings_get_default(), "gtk-double-click-time", &double_click_time, NULL); 999 1000 if (event->button == 2 || 1001 (event->button == 1 && (event->time - lasttime) < double_click_time && lastinfo == partinfo)) { 1002 /* call external program for image, audio or html */ 1003 mimeview_launch(mimeview, partinfo); 1004 return TRUE; 1005 } else if (event->button == 3) { 1006 MainWindow *mainwin = mainwindow_get_mainwindow(); 1007 1008 if (partinfo && (partinfo->type == MIMETYPE_MESSAGE || 1009 partinfo->type == MIMETYPE_IMAGE || 1010 partinfo->type == MIMETYPE_MULTIPART)) 1011 cm_menu_set_sensitive_full(mimeview->ui_manager, "Menus/MimeView/DisplayAsText", FALSE); 1012 else 1013 cm_menu_set_sensitive_full(mimeview->ui_manager, "Menus/MimeView/DisplayAsText", TRUE); 1014 1015 if (partinfo && (partinfo->type == MIMETYPE_MESSAGE || 1016 partinfo->type == MIMETYPE_IMAGE || 1017 partinfo->type == MIMETYPE_TEXT)) 1018 cm_menu_set_sensitive_full(mimeview->ui_manager, "Menus/MimeView/Copy", TRUE); 1019 else 1020 cm_menu_set_sensitive_full(mimeview->ui_manager, "Menus/MimeView/Copy", FALSE); 1021 if (partinfo && 1022 partinfo->type == MIMETYPE_APPLICATION && 1023 !g_ascii_strcasecmp(partinfo->subtype, "octet-stream")) 1024 cm_menu_set_sensitive_full(mimeview->ui_manager, "Menus/MimeView/Open", FALSE); 1025 else 1026 cm_menu_set_sensitive_full(mimeview->ui_manager, "Menus/MimeView/Open", TRUE); 1027 if (mainwin) 1028 main_window_set_menu_sensitive(mainwin); 1029 g_object_set_data(G_OBJECT(mimeview->popupmenu), 1030 "pop_partinfo", partinfo); 1031 1032 gtk_menu_popup_at_pointer(GTK_MENU(mimeview->popupmenu), 1033 (GdkEvent *)event); 1034 return TRUE; 1035 } 1036 1037 lastinfo = partinfo; 1038 lasttime = event->time; 1039 return FALSE; 1040 } 1041 1042 void mimeview_select_next_part(MimeView *mimeview) 1043 { 1044 GtkTreeView *ctree = GTK_TREE_VIEW(mimeview->ctree); 1045 GtkTreeModel *model = gtk_tree_view_get_model(ctree); 1046 GtkTreeSelection *selection; 1047 GtkTreeIter iter; 1048 GtkTreePath *path; 1049 MimeInfo *partinfo = NULL; 1050 gboolean has_next; 1051 1052 if (!mimeview->opened) return; 1053 1054 gtk_tree_model_get_iter(model, &iter, mimeview->opened); 1055 path = gtk_tree_model_get_path(model, &iter); 1056 skip: 1057 has_next = mimeview_tree_next(model, path); 1058 if (!has_next) { 1059 has_next = gtk_tree_model_get_iter_first(model, &iter); 1060 gtk_tree_path_free(path); 1061 path = gtk_tree_model_get_path(model, &iter); 1062 } 1063 1064 if (has_next) { 1065 partinfo = mimeview_get_node_part(mimeview, path); 1066 if (partinfo->type == MIMETYPE_MULTIPART || 1067 (!prefs_common.show_inline_attachments && partinfo->id)) 1068 goto skip; 1069 selection = gtk_tree_view_get_selection(ctree); 1070 gtk_tree_selection_select_path(selection, path); 1071 icon_list_toggle_by_mime_info(mimeview, partinfo); 1072 } 1073 1074 gtk_tree_path_free(path); 1075 } 1076 1077 void mimeview_select_prev_part(MimeView *mimeview) 1078 { 1079 GtkTreeView *ctree = GTK_TREE_VIEW(mimeview->ctree); 1080 GtkTreeModel *model = gtk_tree_view_get_model(ctree); 1081 GtkTreeSelection *selection; 1082 GtkTreeIter iter; 1083 GtkTreePath *path; 1084 MimeInfo *partinfo = NULL; 1085 gboolean has_prev; 1086 1087 if (!mimeview->opened) return; 1088 1089 gtk_tree_model_get_iter(model, &iter, mimeview->opened); 1090 path = gtk_tree_model_get_path(model, &iter); 1091 skip: 1092 has_prev = mimeview_tree_prev(mimeview, path); 1093 if (!has_prev) { 1094 while (mimeview_tree_next(model, path)) {} 1095 has_prev = TRUE; 1096 } 1097 1098 if (has_prev) { 1099 partinfo = mimeview_get_node_part(mimeview, path); 1100 if (partinfo->type == MIMETYPE_MULTIPART || 1101 (!prefs_common.show_inline_attachments && partinfo->id)) 1102 goto skip; 1103 selection = gtk_tree_view_get_selection(ctree); 1104 gtk_tree_selection_select_path(selection, path); 1105 icon_list_toggle_by_mime_info(mimeview, partinfo); 1106 } 1107 1108 gtk_tree_path_free(path); 1109 } 1110 1111 #define BREAK_ON_MODIFIER_KEY() \ 1112 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break 1113 1114 /** 1115 * Returns a filename (with path) for an attachment 1116 * \param partinfo The attachment to save 1117 * \param basedir The target directory 1118 * \param number Used for dummy filename if attachment is unnamed 1119 */ 1120 static gchar *mimeview_get_filename_for_part(MimeInfo *partinfo, 1121 const gchar *basedir, 1122 gint number) 1123 { 1124 gchar *fullname; 1125 gchar *filename; 1126 1127 filename = g_strdup(get_part_name(partinfo)); 1128 if (!filename || !*filename) { 1129 g_free(filename); 1130 filename = g_strdup_printf("noname.%d", number); 1131 } 1132 1133 if (!g_utf8_validate(filename, -1, NULL)) { 1134 gchar *tmp = conv_filename_to_utf8(filename); 1135 g_free(filename); 1136 filename = tmp; 1137 } 1138 1139 subst_for_filename(filename); 1140 1141 fullname = g_strconcat 1142 (basedir, G_DIR_SEPARATOR_S, (g_path_is_absolute(filename)) 1143 ? &filename[1] : filename, NULL); 1144 1145 g_free(filename); 1146 filename = conv_filename_from_utf8(fullname); 1147 g_free(fullname); 1148 return filename; 1149 } 1150 1151 /** 1152 * Write a single attachment to file 1153 * \param filename Filename with path 1154 * \param partinfo Attachment to save 1155 */ 1156 static gint mimeview_write_part(const gchar *filename, 1157 MimeInfo *partinfo, 1158 gboolean handle_error) 1159 { 1160 gchar *dir; 1161 gint err; 1162 1163 dir= g_path_get_dirname(filename); 1164 if (!is_dir_exist(dir)) 1165 make_dir_hier(dir); 1166 g_free(dir); 1167 1168 if (is_file_exist(filename)) { 1169 AlertValue aval; 1170 gchar *res; 1171 gchar *tmp; 1172 1173 if (!g_utf8_validate(filename, -1, NULL)) 1174 tmp = conv_filename_to_utf8(filename); 1175 else 1176 tmp = g_strdup(filename); 1177 1178 res = g_strdup_printf(_("Overwrite existing file '%s'?"), 1179 tmp); 1180 g_free(tmp); 1181 aval = alertpanel(_("Overwrite"), res, NULL, _("_No"), 1182 NULL, _("_Yes"), NULL, NULL, ALERTFOCUS_FIRST); 1183 g_free(res); 1184 if (G_ALERTALTERNATE != aval) 1185 return 2; 1186 } 1187 1188 if ((err = procmime_get_part(filename, partinfo)) < 0) { 1189 debug_print("error saving MIME part: %d\n", err); 1190 if (handle_error) 1191 alertpanel_error 1192 (_("Couldn't save the part of multipart message: %s"), 1193 g_strerror(-err)); 1194 return 0; 1195 } 1196 return 1; 1197 } 1198 1199 static AlertValue mimeview_save_all_error_ask(gint n) 1200 { 1201 gchar *message = g_strdup_printf( 1202 _("An error has occurred while saving message part %d. " 1203 "Do you want to cancel saving or ignore error and " 1204 "continue?"), n); 1205 AlertValue av = alertpanel_full(_("Error saving message part"), 1206 message, NULL, _("_Cancel"), NULL, _("Ignore"), NULL, _("Ignore all"), 1207 ALERTFOCUS_FIRST, FALSE, NULL, ALERT_WARNING); 1208 g_free(message); 1209 return av; 1210 } 1211 1212 static void mimeview_save_all_info(gint errors, gint total) 1213 { 1214 AlertValue aval; 1215 char msg[BUFSIZ]; 1216 1217 if (!errors && prefs_common.show_save_all_success) { 1218 snprintf(msg, sizeof(msg), "%d saved.", total); 1219 aval = alertpanel_full("Notice", msg, NULL, "_Close", 1220 NULL, NULL, NULL, NULL, ALERTFOCUS_FIRST, 1221 TRUE, NULL, ALERT_NOTICE); 1222 if (aval & G_ALERTDISABLE) 1223 prefs_common.show_save_all_success = FALSE; 1224 } else if (prefs_common.show_save_all_failure) { 1225 snprintf(msg, sizeof(msg), "%d saved, %d failed.", total-errors, errors); 1226 aval = alertpanel_full("Warning", msg, NULL, "_Close", 1227 NULL, NULL, NULL, NULL, ALERTFOCUS_FIRST, 1228 TRUE, NULL, ALERT_WARNING); 1229 if (aval & G_ALERTDISABLE) 1230 prefs_common.show_save_all_failure = FALSE; 1231 } 1232 } 1233 1234 /** 1235 * Menu callback: Save all attached files 1236 * \param mimeview Current display 1237 */ 1238 static void mimeview_save_all(MimeView *mimeview, gboolean attachments_only) 1239 { 1240 MimeInfo *partinfo; 1241 gchar *dirname; 1242 gchar *startdir = NULL; 1243 gint number = 0, errors = 0; 1244 gboolean skip_errors = FALSE; 1245 1246 if (!mimeview->opened) return; 1247 if (!mimeview->file) return; 1248 if (!mimeview->mimeinfo) return; 1249 1250 partinfo = mimeview->mimeinfo; 1251 if (prefs_common.attach_save_dir && *prefs_common.attach_save_dir) 1252 startdir = g_strconcat(prefs_common.attach_save_dir, G_DIR_SEPARATOR_S, NULL); 1253 else 1254 startdir = g_strdup(get_home_dir()); 1255 1256 manage_window_focus_in(gtk_widget_get_ancestor(mimeview->hbox, GTK_TYPE_WINDOW), NULL, NULL); 1257 dirname = filesel_select_file_save_folder(_("Select destination folder"), startdir); 1258 if (!dirname) { 1259 g_free(startdir); 1260 return; 1261 } 1262 1263 if (!is_dir_exist (dirname)) { 1264 alertpanel_error(_("'%s' is not a directory."), dirname); 1265 g_free(startdir); 1266 g_free(dirname); 1267 return; 1268 } 1269 1270 if (dirname[strlen(dirname)-1] == G_DIR_SEPARATOR) 1271 dirname[strlen(dirname)-1] = '\0'; 1272 1273 /* Skip the first part, that is sometimes DISPOSITIONTYPE_UNKNOWN */ 1274 if (partinfo && partinfo->type == MIMETYPE_MESSAGE) 1275 partinfo = procmime_mimeinfo_next(partinfo); 1276 if (partinfo && partinfo->type == MIMETYPE_MULTIPART) { 1277 partinfo = procmime_mimeinfo_next(partinfo); 1278 if (partinfo && partinfo->type == MIMETYPE_TEXT) 1279 partinfo = procmime_mimeinfo_next(partinfo); 1280 } 1281 1282 while (partinfo != NULL) { 1283 if ((!attachments_only && 1284 (partinfo->type != MIMETYPE_MESSAGE && 1285 partinfo->type != MIMETYPE_MULTIPART && 1286 (partinfo->disposition != DISPOSITIONTYPE_INLINE || 1287 get_real_part_name(partinfo) != NULL))) || 1288 (attachments_only && 1289 (partinfo->disposition == DISPOSITIONTYPE_ATTACHMENT && 1290 get_real_part_name(partinfo) != NULL))) { 1291 gchar *filename = mimeview_get_filename_for_part( 1292 partinfo, dirname, number++); 1293 1294 gint ok = mimeview_write_part(filename, partinfo, FALSE); 1295 g_free(filename); 1296 if (ok < 1) { 1297 ++errors; 1298 if (!skip_errors) { 1299 AlertValue av = mimeview_save_all_error_ask(number); 1300 skip_errors = (av == G_ALERTOTHER); 1301 if (av == G_ALERTDEFAULT) /* cancel */ 1302 break; 1303 } 1304 } else if (ok == 2) 1305 number--; 1306 } 1307 partinfo = procmime_mimeinfo_next(partinfo); 1308 } 1309 1310 g_free(prefs_common.attach_save_dir); 1311 g_free(startdir); 1312 prefs_common.attach_save_dir = g_filename_to_utf8(dirname, 1313 -1, NULL, NULL, NULL); 1314 g_free(dirname); 1315 1316 mimeview_save_all_info(errors, number); 1317 } 1318 1319 static MimeInfo *mimeview_get_part_to_use(MimeView *mimeview) 1320 { 1321 MimeInfo *partinfo = NULL; 1322 if (mimeview->spec_part) { 1323 partinfo = mimeview->spec_part; 1324 mimeview->spec_part = NULL; 1325 } else { 1326 partinfo = (MimeInfo *) g_object_get_data 1327 (G_OBJECT(mimeview->popupmenu), 1328 "pop_partinfo"); 1329 g_object_set_data(G_OBJECT(mimeview->popupmenu), 1330 "pop_partinfo", NULL); 1331 if (!partinfo) { 1332 partinfo = mimeview_get_selected_part(mimeview); 1333 } 1334 } 1335 1336 return partinfo; 1337 } 1338 /** 1339 * Menu callback: Save the selected attachment 1340 * \param mimeview Current display 1341 */ 1342 void mimeview_save_as(MimeView *mimeview) 1343 { 1344 gchar *filename; 1345 gchar *filepath = NULL; 1346 gchar *filedir = NULL; 1347 MimeInfo *partinfo; 1348 gchar *partname = NULL; 1349 1350 if (!mimeview->opened) return; 1351 if (!mimeview->file) return; 1352 1353 partinfo = mimeview_get_part_to_use(mimeview); 1354 1355 cm_return_if_fail(partinfo != NULL); 1356 1357 if (get_part_name(partinfo) == NULL) { 1358 return; 1359 } 1360 partname = g_strdup(get_part_name(partinfo)); 1361 1362 if (!g_utf8_validate(partname, -1, NULL)) { 1363 gchar *tmp = conv_filename_to_utf8(partname); 1364 if (!tmp) { 1365 tmp = conv_codeset_strdup(partname, 1366 conv_get_locale_charset_str(), 1367 CS_UTF_8); 1368 } 1369 if (tmp) { 1370 g_free(partname); 1371 partname = tmp; 1372 } 1373 } 1374 1375 subst_for_filename(partname); 1376 1377 if (prefs_common.attach_save_dir && *prefs_common.attach_save_dir) 1378 filepath = g_strconcat(prefs_common.attach_save_dir, 1379 G_DIR_SEPARATOR_S, partname, NULL); 1380 else 1381 filepath = g_strdup(partname); 1382 1383 g_free(partname); 1384 1385 manage_window_focus_in(gtk_widget_get_ancestor(mimeview->hbox, GTK_TYPE_WINDOW), NULL, NULL); 1386 filename = filesel_select_file_save(_("Save as"), filepath); 1387 if (!filename) { 1388 g_free(filepath); 1389 return; 1390 } 1391 1392 mimeview_write_part(filename, partinfo, TRUE); 1393 1394 filedir = g_path_get_dirname(filename); 1395 if (filedir && strcmp(filedir, ".")) { 1396 g_free(prefs_common.attach_save_dir); 1397 prefs_common.attach_save_dir = g_filename_to_utf8(filedir, -1, NULL, NULL, NULL); 1398 } 1399 1400 g_free(filedir); 1401 g_free(filepath); 1402 } 1403 1404 void mimeview_display_as_text(MimeView *mimeview) 1405 { 1406 MimeInfo *partinfo; 1407 1408 if (!mimeview->opened) return; 1409 1410 partinfo = mimeview_get_part_to_use(mimeview); 1411 mimeview_select_mimepart_icon(mimeview, partinfo); 1412 cm_return_if_fail(partinfo != NULL); 1413 mimeview_show_message_part(mimeview, partinfo); 1414 } 1415 1416 void mimeview_launch(MimeView *mimeview, MimeInfo *partinfo) 1417 { 1418 gchar *filename; 1419 gint err; 1420 1421 if (!mimeview->opened) return; 1422 if (!mimeview->file) return; 1423 1424 if (!partinfo) 1425 partinfo = mimeview_get_part_to_use(mimeview); 1426 1427 cm_return_if_fail(partinfo != NULL); 1428 1429 filename = procmime_get_tmp_file_name(partinfo); 1430 1431 if ((err = procmime_get_part(filename, partinfo)) < 0) 1432 alertpanel_error 1433 (_("Couldn't save the part of multipart message: %s"), 1434 g_strerror(-err)); 1435 else 1436 mimeview_view_file(filename, partinfo, NULL, mimeview); 1437 1438 g_free(filename); 1439 } 1440 1441 void mimeview_open_with(MimeView *mimeview) 1442 { 1443 MimeInfo *partinfo; 1444 1445 if (!mimeview) return; 1446 if (!mimeview->opened) return; 1447 if (!mimeview->file) return; 1448 1449 partinfo = mimeview_get_part_to_use(mimeview); 1450 1451 mimeview_open_part_with(mimeview, partinfo, FALSE); 1452 } 1453 1454 static void mimeview_open_part_with(MimeView *mimeview, MimeInfo *partinfo, gboolean automatic) 1455 { 1456 gchar *filename; 1457 gchar *cmd; 1458 gchar *mime_command = NULL; 1459 gchar *content_type = NULL; 1460 gint err; 1461 1462 cm_return_if_fail(partinfo != NULL); 1463 1464 filename = procmime_get_tmp_file_name(partinfo); 1465 1466 if ((err = procmime_get_part(filename, partinfo)) < 0) { 1467 alertpanel_error 1468 (_("Couldn't save the part of multipart message: %s"), 1469 g_strerror(-err)); 1470 g_free(filename); 1471 return; 1472 } 1473 1474 if (!prefs_common.mime_open_cmd_history) 1475 prefs_common.mime_open_cmd_history = 1476 add_history(NULL, prefs_common.mime_open_cmd); 1477 1478 if ((partinfo->type == MIMETYPE_APPLICATION) && 1479 (!g_ascii_strcasecmp(partinfo->subtype, "octet-stream"))) { 1480 /* guess content-type from filename */ 1481 content_type = procmime_get_mime_type(filename); 1482 } 1483 if (content_type == NULL) { 1484 content_type = procmime_get_content_type_str(partinfo->type, 1485 partinfo->subtype); 1486 } 1487 1488 if ((partinfo->type == MIMETYPE_TEXT && !strcmp(partinfo->subtype, "html")) 1489 && prefs_common_get_uri_cmd() && prefs_common.uri_cmd[0]) { 1490 mime_command = g_strdup(prefs_common_get_uri_cmd()); 1491 g_free(content_type); 1492 content_type = NULL; 1493 } else if (partinfo->type != MIMETYPE_TEXT || !prefs_common_get_ext_editor_cmd() 1494 || !prefs_common_get_ext_editor_cmd()[0]) { 1495 mime_command = mailcap_get_command_for_type(content_type, filename); 1496 } else { 1497 mime_command = g_strdup(prefs_common_get_ext_editor_cmd()); 1498 g_free(content_type); 1499 content_type = NULL; 1500 } 1501 if (mime_command == NULL) { 1502 /* try with extension this time */ 1503 g_free(content_type); 1504 content_type = procmime_get_mime_type(filename); 1505 mime_command = mailcap_get_command_for_type(content_type, filename); 1506 } 1507 1508 if (mime_command == NULL) 1509 automatic = FALSE; 1510 1511 if (!automatic) { 1512 gboolean remember = FALSE; 1513 if (content_type != NULL) 1514 cmd = input_dialog_combo_remember 1515 (_("Open with"), 1516 _("Enter the command-line to open file:\n" 1517 "('%s' will be replaced with file name)"), 1518 mime_command ? mime_command : prefs_common.mime_open_cmd, 1519 prefs_common.mime_open_cmd_history, &remember); 1520 else 1521 cmd = input_dialog_combo 1522 (_("Open with"), 1523 _("Enter the command-line to open file:\n" 1524 "('%s' will be replaced with file name)"), 1525 mime_command ? mime_command : prefs_common.mime_open_cmd, 1526 prefs_common.mime_open_cmd_history); 1527 if (cmd && remember) { 1528 mailcap_update_default(content_type, cmd); 1529 } 1530 g_free(mime_command); 1531 } else { 1532 cmd = mime_command; 1533 } 1534 if (cmd) { 1535 mimeview_view_file(filename, partinfo, cmd, mimeview); 1536 g_free(prefs_common.mime_open_cmd); 1537 prefs_common.mime_open_cmd = cmd; 1538 prefs_common.mime_open_cmd_history = 1539 add_history(prefs_common.mime_open_cmd_history, cmd); 1540 } 1541 1542 g_free(content_type); 1543 g_free(filename); 1544 } 1545 1546 static void mimeview_send_to(MimeView *mimeview, MimeInfo *partinfo) 1547 { 1548 GList *attach_file = NULL; 1549 AttachInfo *ainfo = NULL; 1550 gchar *filename; 1551 gint err; 1552 1553 if (!mimeview->opened) return; 1554 if (!mimeview->file) return; 1555 1556 cm_return_if_fail(partinfo != NULL); 1557 1558 filename = procmime_get_tmp_file_name(partinfo); 1559 1560 if (!(err = procmime_get_part(filename, partinfo))) { 1561 ainfo = g_new0(AttachInfo, 1); 1562 ainfo->file = filename; 1563 ainfo->name = g_strdup(get_part_name(partinfo)); 1564 ainfo->content_type = procmime_get_content_type_str( 1565 partinfo->type, partinfo->subtype); 1566 ainfo->charset = g_strdup(procmime_mimeinfo_get_parameter( 1567 partinfo, "charset")); 1568 attach_file = g_list_append(attach_file, ainfo); 1569 1570 compose_new(NULL, NULL, attach_file); 1571 1572 g_free(ainfo->name); 1573 g_free(ainfo->content_type); 1574 g_free(ainfo->charset); 1575 g_free(ainfo); 1576 g_list_free(attach_file); 1577 } else 1578 alertpanel_error 1579 (_("Couldn't save the part of multipart message: %s"), 1580 g_strerror(-err)); 1581 g_free(filename); 1582 } 1583 1584 static void mimeview_view_file(const gchar *filename, MimeInfo *partinfo, 1585 const gchar *cmd, MimeView *mimeview) 1586 { 1587 gchar *p; 1588 gchar buf[BUFFSIZE]; 1589 1590 if (cmd == NULL) 1591 mimeview_open_part_with(mimeview, partinfo, TRUE); 1592 else { 1593 if ((p = strchr(cmd, '%')) && *(p + 1) == 's' && 1594 !strchr(p + 2, '%')) { 1595 g_snprintf(buf, sizeof(buf), cmd, filename); 1596 if (!prefs_common.save_parts_readwrite) 1597 g_chmod(filename, S_IRUSR); 1598 else 1599 g_chmod(filename, S_IRUSR|S_IWUSR); 1600 } else { 1601 g_warning("MIME viewer command-line is invalid: '%s'", cmd); 1602 cmd = NULL; 1603 mimeview_open_part_with(mimeview, partinfo, FALSE); 1604 } 1605 if (cmd != NULL && execute_command_line(buf, TRUE, NULL) != 0) { 1606 if (!prefs_common.save_parts_readwrite) 1607 g_chmod(filename, S_IRUSR|S_IWUSR); 1608 mimeview_open_part_with(mimeview, partinfo, FALSE); 1609 } 1610 } 1611 } 1612 1613 void mimeview_register_viewer_factory(MimeViewerFactory *factory) 1614 { 1615 mimeviewer_factories = g_slist_append(mimeviewer_factories, factory); 1616 } 1617 1618 static gint cmp_viewer_by_factroy(gconstpointer a, gconstpointer b) 1619 { 1620 return ((MimeViewer *) a)->factory == (MimeViewerFactory *) b ? 0 : -1; 1621 } 1622 1623 void mimeview_unregister_viewer_factory(MimeViewerFactory *factory) 1624 { 1625 GSList *mimeview_list, *viewer_list; 1626 1627 for (mimeview_list = mimeviews; mimeview_list != NULL; mimeview_list = g_slist_next(mimeview_list)) { 1628 MimeView *mimeview = (MimeView *) mimeview_list->data; 1629 1630 if (mimeview->mimeviewer && mimeview->mimeviewer->factory == factory) { 1631 mimeview_change_view_type(mimeview, MIMEVIEW_TEXT); 1632 mimeview->mimeviewer = NULL; 1633 } 1634 1635 while ((viewer_list = g_slist_find_custom(mimeview->viewers, factory, cmp_viewer_by_factroy)) != NULL) { 1636 MimeViewer *mimeviewer = (MimeViewer *) viewer_list->data; 1637 1638 mimeviewer->destroy_viewer(mimeviewer); 1639 mimeview->viewers = g_slist_remove(mimeview->viewers, mimeviewer); 1640 } 1641 } 1642 1643 mimeviewer_factories = g_slist_remove(mimeviewer_factories, factory); 1644 } 1645 1646 static gboolean icon_clicked_cb (GtkWidget *button, GdkEventButton *event, MimeView *mimeview) 1647 { 1648 gint num; 1649 MimeInfo *partinfo; 1650 1651 num = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "icon_number")); 1652 partinfo = g_object_get_data(G_OBJECT(button), "partinfo"); 1653 1654 if (event->button == 1) { 1655 icon_selected(mimeview, num, partinfo); 1656 gtk_widget_grab_focus(button); 1657 icon_list_toggle_by_mime_info(mimeview, partinfo); 1658 } 1659 part_button_pressed(mimeview, event, partinfo); 1660 1661 return FALSE; 1662 } 1663 1664 static void icon_selected (MimeView *mimeview, gint num, MimeInfo *partinfo) 1665 { 1666 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(mimeview->ctree)); 1667 GtkTreeIter iter; 1668 GtkTreePath *path; 1669 MimeInfo *curr = NULL; 1670 1671 if (!gtk_tree_model_get_iter_first(model, &iter)) 1672 return; 1673 path = gtk_tree_model_get_path(model, &iter); 1674 1675 do { 1676 gtk_tree_model_get_iter(model, &iter, path); 1677 gtk_tree_model_get(model, &iter, COL_DATA, &curr, -1); 1678 if (curr == partinfo) { 1679 GtkTreeSelection *sel = gtk_tree_view_get_selection( 1680 GTK_TREE_VIEW(mimeview->ctree)); 1681 gtk_tree_selection_select_iter(sel, &iter); 1682 gtk_tree_path_free(path); 1683 return; 1684 } 1685 } while (mimeview_tree_next(model, path)); 1686 1687 gtk_tree_path_free(path); 1688 } 1689 1690 void mimeview_select_mimepart_icon(MimeView *mimeview, MimeInfo *partinfo) 1691 { 1692 icon_list_toggle_by_mime_info(mimeview, partinfo); 1693 icon_selected(mimeview, -1, partinfo); 1694 } 1695 1696 static gint icon_key_pressed(GtkWidget *button, GdkEventKey *event, 1697 MimeView *mimeview) 1698 { 1699 SummaryView *summaryview; 1700 1701 if (!event) return FALSE; 1702 1703 switch (event->keyval) { 1704 case GDK_KEY_y: 1705 BREAK_ON_MODIFIER_KEY(); 1706 mimeview_save_as(mimeview); 1707 return TRUE; 1708 case GDK_KEY_t: 1709 BREAK_ON_MODIFIER_KEY(); 1710 mimeview_display_as_text(mimeview); 1711 return TRUE; 1712 case GDK_KEY_l: 1713 BREAK_ON_MODIFIER_KEY(); 1714 mimeview_launch(mimeview, NULL); 1715 return TRUE; 1716 case GDK_KEY_o: 1717 BREAK_ON_MODIFIER_KEY(); 1718 mimeview_open_with(mimeview); 1719 return TRUE; 1720 case GDK_KEY_a: 1721 BREAK_ON_MODIFIER_KEY(); 1722 mimeview_select_next_part(mimeview); 1723 return TRUE; 1724 default: 1725 break; 1726 } 1727 1728 if (!mimeview->messageview->mainwin) return FALSE; 1729 summaryview = mimeview->messageview->mainwin->summaryview; 1730 return summary_pass_key_press_event(summaryview, event); 1731 } 1732 1733 static gboolean icon_popup_menu(GtkWidget *widget, gpointer data) 1734 { 1735 MimeView *mimeview = (MimeView *)data; 1736 MimeInfo *partinfo = g_object_get_data(G_OBJECT(widget), "partinfo"); 1737 1738 g_object_set_data(G_OBJECT(mimeview->popupmenu), 1739 "pop_partinfo", partinfo); 1740 gtk_menu_popup_at_pointer(GTK_MENU(mimeview->popupmenu), NULL); 1741 return TRUE; 1742 } 1743 1744 static void icon_list_append_icon (MimeView *mimeview, MimeInfo *mimeinfo) 1745 { 1746 GtkWidget *pixmap = NULL; 1747 GtkWidget *grid; 1748 GtkWidget *button; 1749 gchar *tip; 1750 gchar *tiptmp; 1751 const gchar *desc = NULL; 1752 gchar *sigshort = NULL; 1753 gchar *content_type; 1754 StockPixmap stockp; 1755 MimeInfo *partinfo; 1756 1757 if (!prefs_common.show_inline_attachments && mimeinfo->id) 1758 return; 1759 1760 grid = mimeview->icon_grid; 1761 mimeview->icon_count++; 1762 button = gtk_event_box_new(); 1763 1764 gtk_container_set_border_width(GTK_CONTAINER(button), 2); 1765 g_object_set_data(G_OBJECT(button), "icon_number", 1766 GINT_TO_POINTER(mimeview->icon_count)); 1767 g_object_set_data(G_OBJECT(button), "partinfo", 1768 mimeinfo); 1769 1770 switch (mimeinfo->type) { 1771 1772 case MIMETYPE_TEXT: 1773 if (mimeinfo->subtype && !g_ascii_strcasecmp(mimeinfo->subtype, "html")) 1774 stockp = STOCK_PIXMAP_MIME_TEXT_HTML; 1775 else if (mimeinfo->subtype && !g_ascii_strcasecmp(mimeinfo->subtype, "calendar")) 1776 stockp = STOCK_PIXMAP_MIME_TEXT_CALENDAR; 1777 else if (mimeinfo->subtype && (!g_ascii_strcasecmp(mimeinfo->subtype, "x-patch") 1778 || !g_ascii_strcasecmp(mimeinfo->subtype, "x-diff"))) 1779 stockp = STOCK_PIXMAP_MIME_TEXT_PATCH; 1780 else 1781 stockp = STOCK_PIXMAP_MIME_TEXT_PLAIN; 1782 break; 1783 case MIMETYPE_MESSAGE: 1784 stockp = STOCK_PIXMAP_MIME_MESSAGE; 1785 break; 1786 case MIMETYPE_APPLICATION: 1787 if (mimeinfo->subtype && (!g_ascii_strcasecmp(mimeinfo->subtype, "pgp-signature") 1788 || !g_ascii_strcasecmp(mimeinfo->subtype, "x-pkcs7-signature") 1789 || !g_ascii_strcasecmp(mimeinfo->subtype, "pkcs7-signature"))) 1790 stockp = STOCK_PIXMAP_MIME_PGP_SIG; 1791 else if (mimeinfo->subtype && !g_ascii_strcasecmp(mimeinfo->subtype, "pdf")) 1792 stockp = STOCK_PIXMAP_MIME_PDF; 1793 else if (mimeinfo->subtype && !g_ascii_strcasecmp(mimeinfo->subtype, "postscript")) 1794 stockp = STOCK_PIXMAP_MIME_PS; 1795 else 1796 stockp = STOCK_PIXMAP_MIME_APPLICATION; 1797 break; 1798 case MIMETYPE_IMAGE: 1799 stockp = STOCK_PIXMAP_MIME_IMAGE; 1800 break; 1801 case MIMETYPE_AUDIO: 1802 stockp = STOCK_PIXMAP_MIME_AUDIO; 1803 break; 1804 default: 1805 stockp = STOCK_PIXMAP_MIME_UNKNOWN; 1806 break; 1807 } 1808 1809 partinfo = mimeinfo; 1810 while (partinfo != NULL) { 1811 partinfo = procmime_mimeinfo_parent(partinfo); 1812 } 1813 pixmap = stock_pixmap_widget_with_overlay(stockp, 0, OVERLAY_NONE, 6, 3); 1814 1815 gtk_container_add(GTK_CONTAINER(button), pixmap); 1816 if (!desc) { 1817 if (prefs_common.attach_desc) 1818 desc = get_part_description(mimeinfo); 1819 else 1820 desc = get_part_name(mimeinfo); 1821 } 1822 1823 content_type = procmime_get_content_type_str(mimeinfo->type, 1824 mimeinfo->subtype); 1825 1826 tip = g_strconcat("<b>", _("Type:"), " </b>", content_type, 1827 "\n<b>", _("Size:"), " </b>", 1828 to_human_readable((goffset)mimeinfo->length), NULL); 1829 g_free(content_type); 1830 if (desc && *desc) { 1831 gchar *tmp = NULL, *escaped = NULL; 1832 if (!g_utf8_validate(desc, -1, NULL)) { 1833 tmp = conv_filename_to_utf8(desc); 1834 } else { 1835 tmp = g_strdup(desc); 1836 } 1837 escaped = g_markup_escape_text(tmp,-1); 1838 1839 tiptmp = g_strconcat(tip, "\n<b>", 1840 prefs_common.attach_desc && mimeinfo->description ? 1841 _("Description:") : _("Filename:"), 1842 " </b>", escaped, NULL); 1843 g_free(tip); 1844 tip = tiptmp; 1845 g_free(tmp); 1846 g_free(escaped); 1847 } 1848 if (sigshort && *sigshort) { 1849 gchar *sigshort_escaped = 1850 g_markup_escape_text(sigshort, -1); 1851 1852 tiptmp = g_strjoin("\n", tip, sigshort_escaped, NULL); 1853 g_free(tip); 1854 g_free(sigshort_escaped); 1855 tip = tiptmp; 1856 } 1857 1858 gtk_widget_set_tooltip_markup(button, tip); 1859 g_free(tip); 1860 gtk_widget_show_all(button); 1861 1862 g_signal_connect(G_OBJECT(button), "popup-menu", 1863 G_CALLBACK(icon_popup_menu), mimeview); 1864 g_signal_connect(G_OBJECT(button), "button_release_event", 1865 G_CALLBACK(icon_clicked_cb), mimeview); 1866 g_signal_connect(G_OBJECT(button), "key_press_event", 1867 G_CALLBACK(icon_key_pressed), mimeview); 1868 gtk_container_add(GTK_CONTAINER(grid), button); 1869 } 1870 1871 static void icon_list_clear (MimeView *mimeview) 1872 { 1873 GList *child, *orig; 1874 GtkAdjustment *adj; 1875 1876 orig = gtk_container_get_children(GTK_CONTAINER(mimeview->icon_grid)); 1877 for (child = orig; child != NULL; child = g_list_next(child)) { 1878 gtk_container_remove(GTK_CONTAINER(mimeview->icon_grid), 1879 GTK_WIDGET(child->data)); 1880 } 1881 g_list_free(orig); 1882 mimeview->icon_count = 0; 1883 adj = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(mimeview->icon_scroll)); 1884 gtk_adjustment_set_value(adj, gtk_adjustment_get_lower(adj)); 1885 } 1886 1887 /*! 1888 *\brief Used to 'click' the next or previous icon. 1889 * 1890 *\return true if the icon 'number' exists and was selected. 1891 */ 1892 static void icon_scroll_size_allocate_cb(GtkWidget *widget, 1893 GtkAllocation *size, MimeView *mimeview) 1894 { 1895 GtkAllocation grid_size; 1896 GtkAllocation layout_size; 1897 GtkAdjustment *adj; 1898 guint width; 1899 guint height; 1900 1901 adj = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(mimeview->icon_scroll)); 1902 1903 gtk_widget_get_allocation(mimeview->icon_grid, &grid_size); 1904 gtk_widget_get_allocation(mimeview->icon_scroll, &layout_size); 1905 1906 gtk_layout_get_size(GTK_LAYOUT(mimeview->icon_scroll), &width, &height); 1907 gtk_layout_set_size(GTK_LAYOUT(mimeview->icon_scroll), 1908 MIN(grid_size.width, layout_size.width), 1909 MAX(grid_size.height, layout_size.height)); 1910 gtk_adjustment_set_step_increment(adj, 10); 1911 } 1912 1913 static void icon_list_create(MimeView *mimeview, MimeInfo *mimeinfo) 1914 { 1915 icon_list_clear(mimeview); 1916 while (mimeinfo != NULL) { 1917 if (mimeinfo->type != MIMETYPE_MULTIPART) 1918 icon_list_append_icon(mimeview, mimeinfo); 1919 if (mimeinfo->node->children != NULL) 1920 icon_list_create(mimeview, 1921 (MimeInfo *) mimeinfo->node->children->data); 1922 mimeinfo = mimeinfo->node->next != NULL 1923 ? (MimeInfo *) mimeinfo->node->next->data 1924 : NULL; 1925 } 1926 1927 gint min_width, width; 1928 gtk_widget_get_preferred_width(mimeview->icon_mainbox, &min_width, &width); 1929 if (min_width < width) { 1930 gtk_widget_set_size_request(mimeview->icon_mainbox, 1931 min_width, -1); 1932 } 1933 if (mimeview->opened) 1934 icon_list_toggle_by_mime_info(mimeview, 1935 mimeview_get_node_part(mimeview, mimeview->opened)); 1936 } 1937 1938 static void icon_list_toggle_by_mime_info (MimeView *mimeview, 1939 MimeInfo *mimeinfo) 1940 { 1941 GList *children, *child; 1942 1943 children = gtk_container_get_children(GTK_CONTAINER(mimeview->icon_grid)); 1944 for (child = children; child != NULL; child = g_list_next(child)) { 1945 gboolean *highlight = NULL; 1946 GtkWidget *icon = gtk_bin_get_child(GTK_BIN(child->data)); 1947 1948 if (!GTK_IS_EVENT_BOX(child->data)) 1949 continue; 1950 1951 highlight = g_object_get_data(G_OBJECT(icon), "highlight"); 1952 *highlight = (g_object_get_data(G_OBJECT(child->data), 1953 "partinfo") == (gpointer)mimeinfo); 1954 1955 gtk_widget_queue_draw(icon); 1956 } 1957 g_list_free(children); 1958 } 1959 1960 static void ctree_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, 1961 MimeView *mimeview) 1962 { 1963 prefs_common.mimeview_tree_height = allocation->height; 1964 } 1965 1966 static gint mime_toggle_button_cb(GtkWidget *button, GdkEventButton *event, 1967 MimeView *mimeview) 1968 { 1969 g_object_ref(button); 1970 1971 mimeview->ctree_mode = !mimeview->ctree_mode; 1972 if (mimeview->ctree_mode) { 1973 gtk_image_set_from_icon_name(GTK_IMAGE(mimeview->arrow), 1974 "pan-end-symbolic", GTK_ICON_SIZE_MENU); 1975 gtk_widget_hide(mimeview->icon_mainbox); 1976 gtk_widget_show(mimeview->ctree_mainbox); 1977 gtk_paned_set_position(GTK_PANED(mimeview->paned), 1978 prefs_common.mimeview_tree_height); 1979 1980 gtk_container_remove(GTK_CONTAINER(mimeview->icon_mainbox), 1981 button); 1982 gtk_box_pack_end(GTK_BOX(mimeview->ctree_mainbox), 1983 button, FALSE, FALSE, 0); 1984 } else { 1985 gtk_image_set_from_icon_name(GTK_IMAGE(mimeview->arrow), 1986 "pan-start-symbolic", GTK_ICON_SIZE_MENU); 1987 gtk_widget_hide(mimeview->ctree_mainbox); 1988 gtk_widget_show(mimeview->icon_mainbox); 1989 gtk_paned_set_position(GTK_PANED(mimeview->paned), 0); 1990 1991 gtk_container_remove(GTK_CONTAINER(mimeview->ctree_mainbox), 1992 button); 1993 gtk_box_pack_start(GTK_BOX(mimeview->icon_mainbox), 1994 button, FALSE, FALSE, 0); 1995 gtk_box_reorder_child(GTK_BOX(gtk_widget_get_parent(button)), button, 0); 1996 if (mimeview->opened) 1997 icon_list_toggle_by_mime_info(mimeview, 1998 mimeview_get_node_part(mimeview, mimeview->opened)); 1999 summary_grab_focus(mimeview->mainwin->summaryview); 2000 } 2001 g_object_unref(button); 2002 return TRUE; 2003 } 2004 2005 void mimeview_update (MimeView *mimeview) 2006 { 2007 if (mimeview && mimeview->mimeinfo) { 2008 icon_list_create(mimeview, mimeview->mimeinfo); 2009 } 2010 } 2011 2012 void mimeview_handle_cmd(MimeView *mimeview, const gchar *cmd, GdkEventButton *event, gpointer data) 2013 { 2014 MessageView *msgview = NULL; 2015 MainWindow *mainwin = NULL; 2016 2017 if (!cmd) 2018 return; 2019 2020 msgview = mimeview->messageview; 2021 if (!msgview) 2022 return; 2023 2024 mainwin = msgview->mainwin; 2025 if (!mainwin) 2026 return; 2027 2028 g_object_set_data(G_OBJECT(mimeview->popupmenu), 2029 "pop_partinfo", NULL); 2030 2031 if (!strcmp(cmd, "cm://view_log")) 2032 log_window_show(mainwin->logwin); 2033 else if (!strcmp(cmd, "cm://save_as")) 2034 mimeview_save_as(mimeview); 2035 else if (!strcmp(cmd, "cm://display_as_text")) 2036 mimeview_display_as_text(mimeview); 2037 else if (!strcmp(cmd, "cm://open_with")) 2038 mimeview_open_with(mimeview); 2039 else if (!strcmp(cmd, "cm://open")) 2040 mimeview_launch(mimeview, NULL); 2041 else if (!strcmp(cmd, "cm://select_attachment") && data != NULL) { 2042 icon_list_toggle_by_mime_info(mimeview, (MimeInfo *)data); 2043 icon_selected(mimeview, -1, (MimeInfo *)data); 2044 } else if (!strcmp(cmd, "cm://open_attachment") && data != NULL) { 2045 mimeview_launch(mimeview, (MimeInfo *)data); 2046 } else if (!strcmp(cmd, "cm://menu_attachment") && data != NULL) { 2047 mimeview->spec_part = (MimeInfo *)data; 2048 part_button_pressed(mimeview, event, (MimeInfo *)data); 2049 } 2050 }