1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #ifdef HAVE_CONFIG_H 28 #include <config.h> 29 #endif 30 31 #include <dirent.h> 32 #include <sys/stat.h> 33 #include <sys/types.h> 34 #include <string.h> 35 #include <stdlib.h> 36 #include <stdio.h> 37 #include <errno.h> 38 39 #include <gtk/gtk.h> 40 #include <gconf/gconf-client.h> 41 #include <libgnome/libgnome.h> 42 #include <libgnomeui/libgnomeui.h> 43 #include <libgnomevfs/gnome-vfs.h> 44 #include <libgnomevfs/gnome-vfs-mime.h> 45 #include <glib.h> 46 #include <glib/gi18n.h> 47 #include <glade/glade.h> 48 49 #include "fsexam.h" 50 #include "fsexam-header.h" 51 #include "encoding.h" 52 #include "fsexam-history.h" 53 #include "fsexam-ui.h" 54 #include "callbacks.h" 55 #include "fsexam-dialog.h" 56 #include "fsexam-dnd.h" 57 58 FSEXAM_view *view; // global variable 59 60 typedef struct _TreeItem TreeItem; 61 struct _TreeItem 62 { 63 GdkPixbuf *icon; // file icon 64 gchar *label; // filename 65 gboolean loaded; // meaningful only for directory 66 TreeItem *children; 67 }; 68 69 static gchar *get_root_dir_name (const gchar *dir); 70 static void set_root_dir (const gchar *dir); 71 static void fsexam_treeitem_free (TreeItem *); 72 static gint get_dir_elements (DIR *dirp); 73 static void fsexam_treeview_set_title (const gchar *); 74 static gint set_item_collation (gconstpointer a, gconstpointer b); 75 static void treemodel_create_with_treeitem ( 76 GtkTreeStore *, 77 TreeItem *, 78 GtkTreeIter, 79 gboolean); 80 static gchar *get_last_path (GtkTextBuffer *, GtkTextIter *); 81 static void common_construct_ui (); 82 static GString *fsexam_filename_get_path (GtkTreeModel *, GtkTreeIter); 83 84 static GdkPixbuf *get_file_pixbuf (const gchar *); 85 static TreeItem *fsexam_treeitem_create (const gchar *); 86 static TreeItem *fsexam_treeitem_create_from_list (GList *); 87 static GtkTreeModel *fsexam_treemodel_create_with_treeitem (TreeItem *root); 88 89 static void 90 mainwin_destroy (GtkObject *object, gpointer user_data) 91 { 92 force_quit = TRUE; 93 fsexam_cleanup_all (); 94 95 gtk_main_quit (); 96 97 return; 98 } 99 100 static gboolean 101 mainwin_delete (GtkObject *object, gpointer user_data) 102 { 103 return FALSE; /* return TRUE will not send 'destroy' signal */ 104 } 105 106 /* 107 * dir can be NULL, ""(for file list), and abs_path 108 */ 109 static void 110 set_root_dir (const gchar *path) 111 { 112 gchar *uri = NULL; 113 gchar *dir = (gchar *)path; 114 115 g_free (view->rootdir); //free old rootdir first 116 117 if (dir == NULL) { 118 view->rootdir = NULL; 119 return; 120 } 121 122 if (! g_file_test (path, G_FILE_TEST_IS_DIR)) { 123 dir = g_path_get_dirname (path); 124 125 if ((*dir == '\0') || (g_utf8_validate (dir, -1, NULL))) { 126 view->rootdir = g_strdup (dir); 127 return; 128 } 129 130 uri = g_filename_to_uri (dir, NULL, NULL); 131 view->rootdir = uri; 132 133 g_free (dir); 134 }else{ 135 if ((*dir == '\0') || (g_utf8_validate (dir, -1, NULL))) { 136 view->rootdir = g_strdup (dir); 137 return; 138 } 139 140 uri = g_filename_to_uri (dir, NULL, NULL); 141 view->rootdir = uri; 142 } 143 144 return; 145 } 146 147 /* 148 * Get the root dirname, may need convert from uri. 149 * free when don't need again. 150 */ 151 static gchar * 152 get_root_dir_name (const gchar *rootdir) 153 { 154 if (NULL == rootdir) 155 return NULL; 156 157 if (*rootdir == 'f') /* file:///... */ 158 return g_filename_from_uri (rootdir, NULL, NULL); 159 else 160 return g_strdup (rootdir); 161 } 162 163 static gboolean 164 button_press (GtkWidget *widget, 165 GdkEventButton *button, 166 gpointer data) 167 { 168 g_return_val_if_fail (button != NULL, FALSE); 169 170 if (button->button == 1 && button->type == GDK_2BUTTON_PRESS) { 171 cb_convert (); 172 173 return TRUE; 174 }else if (button->button == 2) { 175 fsexam_content_peek (button->x_root, button->y_root); 176 177 return TRUE; 178 }else if (button->button == 3) { 179 GtkWidget *menu = view->popup_menu; 180 GtkWidget *w = NULL; 181 gboolean sensitive; 182 183 /* undo menu */ 184 w = g_object_get_data (G_OBJECT (menu), "popup_undo"); 185 gtk_widget_set_sensitive (w, view->undo_list ? TRUE : FALSE); 186 187 /* conversion mode menu */ 188 w = g_object_get_data (G_OBJECT (menu), 189 view->setting->pref->conv_content ? "popup_content_mode" 190 : "popup_name_mode"); 191 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE); 192 193 /* scenario menu */ 194 w = g_object_get_data (G_OBJECT (view->mainwin), "menu_scenario"); 195 sensitive = GTK_WIDGET_IS_SENSITIVE (w); 196 w = g_object_get_data (G_OBJECT (menu), "popup_scenario"); 197 gtk_widget_set_sensitive (w, sensitive); 198 //gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), sensitive); 199 200 /* display the popup menu */ 201 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 202 button->button, button->time); 203 204 return TRUE; 205 } 206 207 return FALSE; 208 } 209 210 static gboolean 211 button_release (GtkWidget *widget, GdkEventButton *event, gpointer data) 212 { 213 if (view->peekwin && event->button == 2) { 214 gtk_object_destroy (GTK_OBJECT (view->peekwin)); 215 view->peekwin = NULL; 216 } 217 218 return FALSE; 219 } 220 221 static gboolean 222 key_press (GtkWidget *widget, GdkEventKey *event, gpointer data) 223 { 224 switch (event->keyval) { 225 case GDK_space: 226 cb_convert (); 227 return TRUE; 228 /* case GDK_F12: 229 if (event->state == GDK_SHIFT_MASK) { 230 printf ("shitf+F12\n"); 231 } 232 printf ("F12\n"); 233 return TRUE; */ 234 default: 235 break; 236 } 237 238 return FALSE; 239 } 240 241 /* 242 * Search give string at the same level with given iter 243 */ 244 static GtkTreeIter * 245 search_model_at_same_level (GtkTreeModel *model, 246 GtkTreeIter *iter, 247 const gchar *string) 248 { 249 if ((NULL == string) || (NULL == iter)) 250 return NULL; 251 252 do { 253 gchar *name = NULL; 254 gchar *internal_name = NULL; /* escaped name */ 255 256 /* get the escaped name */ 257 gtk_tree_model_get (model, iter, FILENAME_COLUMN, &internal_name, -1); 258 259 /* unescaped the escaped name */ 260 name = g_strcompress (internal_name); 261 g_free (internal_name); 262 263 if (strcmp (name, string) == 0) { 264 g_free (name); /* found */ 265 266 return gtk_tree_iter_copy (iter); 267 } 268 269 g_free (name); 270 } while (gtk_tree_model_iter_next (model, iter)); 271 272 return NULL; 273 } 274 275 /* 276 * Update GtkTreeView which constructed from file list. 277 * The underlying store is GtkListStore 278 */ 279 static void 280 update_gui_for_filelist (GtkTreeModel *model, 281 const gchar *oldname, 282 const gchar *newname, 283 GtkTreePath *treepath, 284 gboolean is_myself) 285 { 286 GtkTreeIter iter; 287 gint old_name_len; 288 289 if ((NULL == model) || (NULL == oldname) 290 || (NULL == newname) || (NULL == view->basedir)) 291 return; 292 293 if (! fsexam_is_subpath (oldname, view->basedir)) 294 return; 295 296 if (! gtk_tree_model_get_iter_first (model, &iter)) { 297 return; 298 } 299 300 old_name_len = strlen (oldname); 301 302 do { 303 gchar *name = NULL; 304 gchar *internal_name = NULL; 305 gchar *tmp = NULL; 306 307 /* get the filename from TreeView */ 308 gtk_tree_model_get (model, &iter, FILENAME_COLUMN, &internal_name, -1); 309 name = g_strcompress (internal_name); 310 g_free (internal_name); 311 312 /* oldname is prefix path of name ? */ 313 if (g_str_has_prefix (name, oldname)) { 314 tmp = name + old_name_len; 315 316 /* construct new name */ 317 if (*tmp == '\0') { 318 gchar *display_name = NULL; 319 gchar *internal_name = NULL; 320 321 internal_name = g_strescape (newname, ""); 322 display_name = fsexam_filename_display_basename (newname); 323 324 gtk_list_store_set (GTK_LIST_STORE (model), &iter, 325 FILENAME_COLUMN, internal_name, 326 DISPLAYNAME_COLUMN, display_name, 327 -1); 328 329 g_free (display_name); 330 g_free (internal_name); 331 }else if (*tmp == '/') { 332 gchar *new_full_name; 333 gchar *display_name; 334 gchar *internal_name; 335 336 new_full_name = g_strdup_printf ("%s/%s", newname, tmp + 1); 337 display_name = fsexam_filename_display_basename (new_full_name); 338 internal_name = g_strescape (new_full_name, ""); 339 340 gtk_list_store_set (GTK_LIST_STORE (model), &iter, 341 FILENAME_COLUMN, internal_name, 342 DISPLAYNAME_COLUMN, display_name, 343 -1); 344 345 g_free (new_full_name); 346 g_free (display_name); 347 g_free (internal_name); 348 } 349 } 350 351 g_free (name); 352 } while (gtk_tree_model_iter_next (model, &iter)); 353 354 return; 355 } 356 357 /* 358 * Update treeview_file 359 * 360 * is_myself is TRUE if conversion are caused by dir/file TreeView 361 * is_myself is FALSE if conversion are caused by search result Treeview 362 */ 363 static void 364 update_gui_for_dir (GtkTreeModel *model, 365 const gchar *path, 366 const gchar *oldname, 367 const gchar *newname, 368 GtkTreePath *treepath, 369 gboolean is_myself) 370 { 371 GtkTreeIter iter; 372 GtkTreeIter *match_iter = NULL; 373 gchar *rootdir = NULL; 374 gchar *remaining = NULL; 375 gchar *display_name = NULL; 376 gchar *internal_name = NULL; 377 gchar *path_clone = NULL; 378 gint len; 379 380 display_name = fsexam_filename_display_name (newname); 381 internal_name = g_strescape (newname, ""); 382 383 if (is_myself && treepath != NULL) { 384 gtk_tree_model_get_iter (model, &iter, treepath); 385 gtk_tree_store_set (GTK_TREE_STORE (model), 386 &iter, 387 FILENAME_COLUMN, internal_name, 388 DISPLAYNAME_COLUMN, display_name, 389 -1); 390 391 goto CLEAN_UP; 392 } 393 394 rootdir = get_root_dir_name (view->rootdir); 395 396 if (! g_str_has_prefix (path, rootdir)) 397 goto CLEAN_UP; 398 399 path_clone = g_strdup (path); 400 len = strlen (rootdir); 401 remaining = path_clone + len; 402 403 if (! gtk_tree_model_get_iter_first (model, &iter)) { 404 goto CLEAN_UP; /* empty tree */ 405 } 406 407 if (*remaining == '\0') { /* top level */ 408 match_iter = search_model_at_same_level (model, 409 &iter, 410 oldname); 411 412 if (match_iter == NULL) 413 goto CLEAN_UP; 414 415 gtk_tree_store_set (GTK_TREE_STORE (model), match_iter, 416 FILENAME_COLUMN, internal_name, 417 DISPLAYNAME_COLUMN, display_name, 418 -1); 419 420 gtk_tree_iter_free (match_iter); 421 }else if (*remaining == '/') { /* iterate each directory level */ 422 gchar *cur = ++remaining; //skip the '/' 423 gboolean last_element; 424 gboolean loaded; 425 426 while (TRUE) { 427 if ((*remaining != '\0') && (*remaining != '/')) { 428 ++remaining; 429 continue; 430 } 431 432 last_element = (*remaining == '\0') ? TRUE : FALSE; 433 *remaining = '\0'; 434 match_iter = search_model_at_same_level (model, 435 &iter, 436 cur); 437 if (match_iter == NULL) 438 break; 439 440 if (! gtk_tree_model_iter_children (model, &iter, match_iter)) { 441 gtk_tree_iter_free (match_iter); /* no children */ 442 break; 443 } 444 445 /* now iter is the first child of match_iter */ 446 gtk_tree_model_get (model, match_iter, 447 LOADED_COLUMN, &loaded, 448 -1); 449 450 gtk_tree_iter_free (match_iter); 451 452 if (! loaded) { 453 break; 454 }else if (last_element) { 455 match_iter = search_model_at_same_level (model, 456 &iter, oldname); 457 458 if (match_iter != NULL) { 459 gchar *display_name; 460 461 display_name = fsexam_filename_display_name (newname); 462 gtk_tree_store_set ( 463 GTK_TREE_STORE (model), match_iter, 464 FILENAME_COLUMN, internal_name, 465 DISPLAYNAME_COLUMN, display_name, 466 -1); 467 468 gtk_tree_iter_free (match_iter); 469 g_free (display_name); 470 } 471 472 break; 473 } 474 475 cur = ++remaining; 476 } 477 } 478 479 CLEAN_UP: 480 g_free (rootdir); 481 g_free (display_name); 482 g_free (internal_name); 483 g_free (path_clone); 484 485 return; 486 } 487 488 /* 489 * Get the last path of GtkTextBuffer of report pane 490 */ 491 static gchar * 492 get_last_path (GtkTextBuffer *buffer, GtkTextIter *iter) 493 { 494 gchar *text = NULL; 495 gint line; 496 497 line = gtk_text_iter_get_line (iter) - 1; 498 499 while (line >= 0) { 500 GtkTextIter start, end; 501 gtk_text_buffer_get_iter_at_line_offset (buffer, &start, line, 0); 502 gtk_text_buffer_get_iter_at_line_offset (buffer, &end, line + 1, 0); 503 text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); 504 505 if (*text != '\n' && *text != '\t') /* info line */ 506 break; 507 508 --line; 509 g_free (text); 510 } 511 512 if (text == NULL) 513 return NULL; 514 515 *(text + strlen (text) - 1) = '\0'; /* remove '\n' */ 516 517 if (strncmp (text, "file:///", 8) == 0) { 518 gchar *tmp = g_filename_from_uri (text, NULL, NULL); 519 g_free (text); 520 text = tmp; 521 } 522 523 return text; 524 } 525 526 // dummy TreeItem 527 static TreeItem demo_toplevel[] = 528 { 529 { NULL, "", TRUE, NULL}, 530 { NULL } 531 }; 532 533 // copy from gsearchtool to load the pixbuf 534 535 #define ICON_THEME_EXECUTABLE_ICON "gnome-fs-executable" 536 #define ICON_THEME_REGULAR_ICON "gnome-fs-regular" 537 #define ICON_THEME_CHAR_DEVICE "gnome-fs-chardev" 538 #define ICON_THEME_BLOCK_DEVICE "gnome-fs-blockdev" 539 #define ICON_THEME_SOCKET "gnome-fs-socket" 540 #define ICON_THEME_FIFO "gnome-fs-fifo" 541 #define ICON_THEME_SYMLINK "emblem-symbolic-link" 542 #define ICON_SIZE 24 543 544 #if 0 545 static GdkPixbuf * 546 get_file_pixbuf (const gchar *file) 547 { 548 GdkPixbuf *pixbuf = NULL; 549 gchar *icon_name = NULL; 550 gchar *escape_path = NULL; 551 GnomeVFSFileInfo *vfs_file_info = NULL; 552 553 vfs_file_info = gnome_vfs_file_info_new (); 554 escape_path = gnome_vfs_escape_path_string (file); 555 556 gnome_vfs_get_file_info (escape_path, vfs_file_info, 557 GNOME_VFS_FILE_INFO_DEFAULT | 558 GNOME_VFS_FILE_INFO_GET_MIME_TYPE | 559 GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE); 560 561 #endif 562 563 static GdkPixbuf * 564 get_file_pixbuf (const gchar *file) 565 { 566 GdkPixbuf *pixbuf = NULL; 567 gchar *icon_name = NULL; 568 const gchar *mime_type; 569 570 mime_type = gnome_vfs_get_file_mime_type (file, NULL, FALSE); 571 572 if (g_file_test (file, G_FILE_TEST_IS_SYMLINK)) { 573 icon_name = g_strdup (ICON_THEME_SYMLINK); 574 } else if (file == NULL || mime_type == NULL) { 575 icon_name = g_strdup (ICON_THEME_REGULAR_ICON); 576 } else if ((g_file_test (file, G_FILE_TEST_IS_EXECUTABLE)) && 577 !g_ascii_strcasecmp (mime_type, "application/x-executable-binary")) { 578 icon_name = g_strdup (ICON_THEME_EXECUTABLE_ICON); 579 } else if (!g_ascii_strcasecmp (mime_type, "x-special/device-char")) { 580 icon_name = g_strdup (ICON_THEME_CHAR_DEVICE); 581 } else if (!g_ascii_strcasecmp (mime_type, "x-special/device-block")) { 582 icon_name = g_strdup (ICON_THEME_BLOCK_DEVICE); 583 } else if (!g_ascii_strcasecmp (mime_type, "x-special/socket")) { 584 icon_name = g_strdup (ICON_THEME_SOCKET); 585 } else if (!g_ascii_strcasecmp (mime_type, "x-special/fifo")) { 586 icon_name = g_strdup (ICON_THEME_FIFO); 587 } else { 588 icon_name = gnome_icon_lookup ( 589 gtk_icon_theme_get_default (), 590 NULL, file, 591 NULL, NULL, 592 mime_type, 0, 593 NULL); 594 } 595 596 if (icon_name == NULL) 597 return NULL; 598 599 pixbuf = (GdkPixbuf *)g_hash_table_lookup (view->pixbuf_hash, icon_name); 600 601 if (pixbuf == NULL) { 602 pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), 603 icon_name, 604 ICON_SIZE, 0, NULL); 605 606 /* free icon_name when free view->pixbuf_hash */ 607 if (pixbuf != NULL) 608 g_hash_table_insert (view->pixbuf_hash, 609 g_strdup (icon_name), 610 pixbuf); 611 } 612 613 g_free (icon_name); 614 615 return pixbuf; 616 } 617 618 /* 619 * free TreeItem and the memory it points to 620 */ 621 static void 622 fsexam_treeitem_free (TreeItem *item) 623 { 624 TreeItem *item2 = item; 625 626 // As one place holder, demo_toplevel isn't dynamically 627 // generated, we can't free it 628 if (NULL == item || item == demo_toplevel) return; 629 630 while (TRUE) { 631 if (item->label == NULL) /* all remaining TreeItem contain no data */ 632 break; 633 634 g_free (item->label); 635 636 if (item->icon != NULL) 637 g_object_unref (item->icon); 638 639 if (item->children) 640 fsexam_treeitem_free (item->children); 641 642 ++item; 643 } 644 645 g_free (item2); 646 } 647 648 /* 649 * Get the number of elements in given directory 650 */ 651 static gint 652 get_dir_elements (DIR *dirp) 653 { 654 struct dirent *dp = NULL; 655 gint num_elements = 0; 656 657 if (dirp == NULL) 658 return 0; 659 660 while ((dp = readdir (dirp)) != NULL) { 661 ++num_elements; 662 } 663 664 rewinddir (dirp); 665 666 return num_elements; 667 } 668 669 /* 670 * Change the title of treeview to indicate the current root directory 671 * that treeview is based on. 672 */ 673 static void 674 fsexam_treeview_set_title (const gchar *title) 675 { 676 GtkWidget *w; 677 678 w = g_object_get_data (G_OBJECT (view->mainwin), "entry_folder"); 679 gtk_entry_set_text (GTK_ENTRY (w), title); 680 681 return; 682 } 683 684 /* 685 * TODO: collation for non-utf8 686 */ 687 static gint 688 set_item_collation (gconstpointer a, gconstpointer b) 689 { 690 TreeItem *item_a = (TreeItem *)a; 691 TreeItem *item_b = (TreeItem *)b; 692 gint retval; 693 694 if (!item_a->label || !item_b->label) return -1; 695 696 // put directory ahead 697 if (item_a->children && !item_b->children) 698 return -1; 699 if (!item_a->children && item_b->children) 700 return 1; 701 702 retval = g_strcasecmp (item_a->label, item_b->label); 703 704 return (retval == 0 ? (strcmp (item_a->label, item_b->label) > 0 ? -1 : 1) 705 : retval); 706 } 707 708 /* 709 * Create TreeItem list from given directory 710 */ 711 static TreeItem * 712 fsexam_treeitem_create (const gchar *dir) 713 { 714 struct stat stat_buf; 715 struct dirent *dp = NULL; 716 TreeItem *tree = NULL; 717 TreeItem *item = NULL; 718 DIR *dirp; 719 gint count; 720 gchar *msg = NULL; 721 gint filenum = 0; 722 723 if (NULL == dir || lstat (dir, &stat_buf) == -1) 724 goto _ERR; 725 726 if (! S_ISDIR (stat_buf.st_mode)) { 727 tree = g_new0 (TreeItem, 2); 728 tree->icon = get_file_pixbuf (dir); 729 tree->label = g_path_get_basename (dir); 730 tree->loaded = 1; 731 732 return tree; 733 } 734 735 dirp = opendir (dir); 736 if (dirp == NULL) 737 goto _ERR; 738 739 filenum = get_dir_elements (dirp); //contain '.' and '..' 740 tree = g_new0 (TreeItem, filenum); 741 count = 0; 742 743 while (((dp = readdir (dirp)) != NULL)) { 744 gchar *name = NULL; 745 746 if ((strcmp (dp->d_name, ".") == 0) 747 || (strcmp (dp->d_name, "..") == 0)) 748 continue; 749 750 name = g_strdup_printf ("%s/%s", dir, dp->d_name); 751 752 item = tree + count; 753 item->label = g_strdup (dp->d_name); 754 item->loaded = 1; 755 item->icon = get_file_pixbuf (name); 756 757 if (lstat (name, &stat_buf) == 0 && S_ISDIR (stat_buf.st_mode)) { 758 // mark it is unloaded yet and put demo_toplevel as its children 759 // to act as one placeholder 760 item->loaded = 0; 761 item->children = demo_toplevel; 762 } 763 764 g_free (name); 765 ++count; 766 } 767 768 closedir (dirp); 769 770 qsort (tree, count, sizeof (TreeItem), set_item_collation); 771 772 return tree; 773 774 _ERR: 775 switch (errno) { 776 case EACCES: 777 msg = g_strdup_printf (_("No read permission")); 778 break; 779 case ENOENT: 780 msg = g_strdup_printf (_("File doesn't exist")); 781 break; 782 } 783 784 if (!dir) 785 msg = g_strdup_printf (_("No folder specified")); 786 787 if (msg) { 788 GtkWidget *notebook = NULL; 789 790 /* show conversion log notebook page */ 791 notebook = g_object_get_data (G_OBJECT (view->mainwin), 792 "notebook_report"); 793 gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), 0); 794 795 fsexam_statusbar_update (msg); 796 fsexam_gui_display_msg (dir, msg); 797 g_free (msg); 798 } 799 800 return demo_toplevel; 801 } 802 803 /* 804 * Create TreeItem list from file list 805 */ 806 static TreeItem * 807 fsexam_treeitem_create_from_list (GList *files) 808 { 809 TreeItem *tree = NULL; 810 TreeItem *item = NULL; 811 struct stat buf; 812 GList *current; 813 814 tree = g_new0 (TreeItem, g_list_length (files) + 1); 815 item = tree; 816 current = files; 817 818 while (current != NULL) { 819 gchar *filename = current->data; 820 821 current = g_list_next (current); 822 823 if ((lstat (filename, &buf) < 0)) { 824 fsexam_gui_display_msg (filename, 825 _("File does not exist or no access permission.")); 826 827 continue; 828 }else if (!(S_ISREG(buf.st_mode)) 829 && !(S_ISDIR(buf.st_mode)) 830 && !(S_ISLNK(buf.st_mode))) { 831 fsexam_gui_display_msg (filename, _("File type is not supported.")); 832 continue; 833 } 834 835 item->label = get_abs_path (filename); 836 item->loaded = 1; 837 item->icon = get_file_pixbuf (filename); 838 839 item ++; 840 } 841 842 return tree; 843 } 844 845 /* create GtkTreeModel from TreeItem list */ 846 static void 847 treemodel_create_with_treeitem (GtkTreeStore *model, 848 TreeItem *item, 849 GtkTreeIter parent_iter, 850 gboolean is_root) 851 { 852 GtkTreeIter iter; 853 gchar *display_name; 854 gchar *internal_name; 855 856 if (NULL == item) 857 return; 858 859 while (item->label != NULL) { 860 TreeItem *subitem = item->children; 861 display_name = fsexam_filename_display_name (item->label); 862 internal_name = g_strescape (item->label, ""); 863 864 if (is_root) 865 gtk_tree_store_append(model, &iter, NULL); 866 else 867 gtk_tree_store_append(model, &iter, &parent_iter); 868 869 gtk_tree_store_set (model, &iter, 870 ICON_COLUMN, item->icon, 871 FILENAME_COLUMN, internal_name, 872 DISPLAYNAME_COLUMN, display_name, 873 LOADED_COLUMN, item->loaded, 874 -1); 875 876 if (subitem) 877 treemodel_create_with_treeitem(model, subitem, iter, FALSE); 878 879 g_free (display_name); 880 g_free (internal_name); 881 item++; 882 } 883 884 return; 885 } 886 887 /* 888 * Concatenate each part to generate its full path name 889 * The returned GString should be freed with g_string_free () 890 * 891 * please be sure that the third param is view->rootdir 892 */ 893 static GString * 894 fsexam_filename_get_path (GtkTreeModel *model, GtkTreeIter iter) 895 { 896 GtkTreeIter parent; 897 GString *dir; 898 gchar *fullpath = get_root_dir_name (view->rootdir); 899 900 dir = g_string_new (NULL); 901 902 while (gtk_tree_model_iter_parent (model, &parent, &iter)) { 903 gchar *filename = NULL; 904 gchar *internal_name = NULL; 905 906 gtk_tree_model_get (model, &parent, 907 FILENAME_COLUMN, &internal_name, 908 -1); 909 910 filename = g_strcompress (internal_name); 911 dir = g_string_prepend (dir, filename); 912 dir = g_string_prepend (dir, "/"); 913 914 g_free (filename); 915 g_free (internal_name); 916 iter = parent; 917 } 918 919 if (fullpath) { 920 // if it ends with '/', modify it to avoid two consecutive slashes 921 gchar *ch = fullpath + strlen (fullpath) - 1; 922 923 if (*ch == '/') 924 *ch = 0; 925 926 dir = g_string_prepend (dir, fullpath); 927 } 928 929 g_free (fullpath); 930 931 return dir; 932 } 933 934 /* 935 * Concatenate each part to generate its full file name 936 * The returned GString should be freed with g_string_free () 937 */ 938 gchar * 939 fsexam_filename_get_fullname (GtkTreeModel *model, GtkTreeIter *iter) 940 { 941 GString *fullname = NULL; 942 gchar *filename = NULL; 943 gchar *internal_name = NULL; 944 945 gtk_tree_model_get (model, iter, 946 FILENAME_COLUMN, &internal_name, 947 -1); 948 949 filename = g_strcompress (internal_name); 950 g_free (internal_name); 951 952 if (*filename == '/') { /* search result treeview */ 953 return filename; 954 } 955 956 /* upper-left corner treeview, not search result treeview */ 957 fullname = fsexam_filename_get_path (model, *iter); 958 fullname = g_string_append (fullname, "/"); 959 fullname = g_string_append (fullname, filename); 960 961 g_free (filename); 962 filename = g_string_free (fullname, FALSE); 963 964 return filename; 965 } 966 967 968 969 /* 970 * Check if it is one directory which hasn't been loaded yet, 971 * if yes, load its files and add into model. 972 * If permission issue exists, collapse the directory row and 973 * write error message to statusbar. 974 */ 975 gboolean 976 fsexam_treeview_expand (GtkWidget *widget, 977 GtkTreeIter *iter, 978 GtkTreePath *path, 979 gpointer user_data) 980 { 981 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); 982 gboolean loaded; 983 gchar *filename = NULL; 984 gchar *internal_name = NULL; 985 986 gtk_tree_model_get (model, iter, 987 FILENAME_COLUMN, &internal_name, 988 LOADED_COLUMN, &loaded, 989 -1); 990 991 filename = g_strcompress (internal_name); 992 g_free (internal_name); 993 994 if (!loaded) { 995 GtkTreeIter dummy_iter; 996 TreeItem *item = NULL; 997 GString *dir = NULL; 998 gchar *name = NULL; 999 1000 dir = fsexam_filename_get_path (model, *iter); 1001 name = g_strdup_printf ("%s/%s", dir->str, filename); 1002 1003 item = fsexam_treeitem_create (name); 1004 1005 if (item == demo_toplevel) { 1006 fsexam_statusbar_update (_("No read permission")); 1007 gtk_tree_view_collapse_row (GTK_TREE_VIEW (widget), path); 1008 } else if (!item->label) { 1009 fsexam_statusbar_update (_("Empty folder")); 1010 gtk_tree_view_collapse_row (GTK_TREE_VIEW (widget), path); 1011 } else { 1012 treemodel_create_with_treeitem (GTK_TREE_STORE (model), 1013 item, 1014 *iter, 1015 FALSE); 1016 1017 // change it's status 1018 gtk_tree_store_set (GTK_TREE_STORE (model), iter, 1019 LOADED_COLUMN, TRUE, 1020 -1); 1021 1022 // dummy_iter will be the first child of iter. 1023 // it is demo_toplevel, need remove it 1024 gtk_tree_model_iter_children (model, &dummy_iter, iter); 1025 gtk_tree_store_remove (GTK_TREE_STORE (model), &dummy_iter); 1026 } 1027 1028 g_string_free (dir, TRUE); 1029 free(name); 1030 fsexam_treeitem_free (item); 1031 } 1032 1033 g_free (filename); 1034 1035 return TRUE; 1036 } 1037 1038 /* 1039 * Create GtkTreeModel using given TreeItem list 1040 */ 1041 static GtkTreeModel * 1042 fsexam_treemodel_create_with_treeitem (TreeItem *root) 1043 { 1044 GtkTreeStore *model; 1045 GtkTreeIter iter; 1046 1047 if (root == NULL) 1048 return NULL; 1049 1050 model = gtk_tree_store_new (NUM_COLUMNS, 1051 GDK_TYPE_PIXBUF, 1052 G_TYPE_STRING, 1053 G_TYPE_STRING, 1054 G_TYPE_BOOLEAN); 1055 1056 treemodel_create_with_treeitem (model, root, iter, TRUE); 1057 1058 return GTK_TREE_MODEL (model); 1059 } 1060 1061 /* 1062 * create GtkTreeView from single directory 1063 * 1064 * dir need to be absolute path 1065 */ 1066 gboolean 1067 fsexam_treeview_construct (const gchar *dir) 1068 { 1069 TreeItem *toplevel; 1070 GtkWidget *widget; 1071 GtkTreeModel *model; 1072 GtkTreeSelection *selection; 1073 GtkTreeIter iter; 1074 gchar *abs_path = NULL; 1075 1076 if (dir == NULL) 1077 return FALSE; 1078 1079 if (g_file_test (dir, G_FILE_TEST_IS_SYMLINK)) { 1080 abs_path = get_abs_path_for_symlink_target (dir); 1081 } else { 1082 abs_path = get_abs_path (dir); 1083 } 1084 1085 if (abs_path == NULL) { 1086 fsexam_gui_display_msg (dir, 1087 g_file_test (dir, G_FILE_TEST_IS_SYMLINK) ? 1088 _("Symlink target doesn't exist") : 1089 _("doesn't exist")); 1090 fsexam_gui_display_stats (view->setting); 1091 1092 return FALSE; 1093 } 1094 1095 /* Create TreeItem from directory name */ 1096 if ((toplevel = fsexam_treeitem_create (abs_path)) == demo_toplevel) { 1097 g_free (abs_path); 1098 return FALSE; 1099 } 1100 1101 set_root_dir (abs_path); 1102 1103 widget = g_object_get_data (G_OBJECT (view->mainwin), "treeview_file"); 1104 1105 /* Create GtkTreeModel from TreeItem list */ 1106 model = fsexam_treemodel_create_with_treeitem (toplevel); 1107 gtk_tree_view_set_model (GTK_TREE_VIEW (widget), model); 1108 fsexam_treeitem_free (toplevel); 1109 1110 /* select the first row by default */ 1111 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)); 1112 if (gtk_tree_model_get_iter_first (model, &iter)) 1113 gtk_tree_selection_select_iter (selection, &iter); 1114 1115 if (view->rootdir) 1116 fsexam_treeview_set_title (view->rootdir); 1117 1118 g_free (abs_path); 1119 1120 return TRUE; 1121 } 1122 1123 /* create GtkTreeView from multiple files */ 1124 void 1125 fsexam_treeview_construct_from_list (GList *list) 1126 { 1127 TreeItem *toplevel; 1128 GtkWidget *widget; 1129 GtkTreeModel *model; 1130 GtkTreeIter iter; 1131 GtkTreeSelection *selection; 1132 1133 if (NULL == list) 1134 return; 1135 1136 g_free (view->rootdir); 1137 view->rootdir = g_strdup (FSEXAM_ROOT_DIR); 1138 fsexam_treeview_set_title (view->rootdir); 1139 1140 widget = g_object_get_data (G_OBJECT (view->mainwin), "treeview_file"); 1141 toplevel = fsexam_treeitem_create_from_list (list); 1142 model = fsexam_treemodel_create_with_treeitem (toplevel); 1143 gtk_tree_view_set_model (GTK_TREE_VIEW (widget), model); 1144 fsexam_treeitem_free (toplevel); 1145 1146 /* select the first row by default */ 1147 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)); 1148 gtk_tree_model_get_iter_first (model, &iter); 1149 gtk_tree_selection_select_iter (selection, &iter); 1150 1151 return; 1152 } 1153 1154 void 1155 fsexam_statusbar_update (gchar *msg) 1156 { 1157 GtkWidget *statusbar; 1158 1159 statusbar = g_object_get_data (G_OBJECT (view->mainwin), "appbar"); 1160 gnome_appbar_pop (GNOME_APPBAR (statusbar)); 1161 gnome_appbar_push (GNOME_APPBAR(statusbar), msg); 1162 1163 return; 1164 } 1165 1166 /* 1167 * Clear statusbar 1168 */ 1169 gboolean 1170 fsexam_statusbar_fresh () 1171 { 1172 fsexam_statusbar_update (""); 1173 1174 return TRUE; 1175 } 1176 1177 /* 1178 * Get the filename from GtkFileChooser 1179 * 1180 * Ask to convert to UTF-8 if filename is not UTF-8 encoded. 1181 */ 1182 gchar * 1183 fsexam_file_chooser_get_name (const gchar *title, 1184 GtkFileChooserAction action, 1185 gboolean no_check, 1186 gboolean ensure_utf8) 1187 { 1188 GtkWidget *dialog; 1189 gchar *filename = NULL; 1190 const gchar *emp_msg = N_("The selected file is not empty, do you want to override it?"); 1191 const gchar *nonutf8_msg = N_("The selected file is not UTF-8 encoded, we strongly suggest you to use UTF-8 filename.\n\n Do you want to convert it to UTF8?"); 1192 const gchar *converr_msg = N_("Can't convert %s to UTF8: %s.\n\nPlease Note that there may exist partial conversion.\n So please Refresh you directory and select file again."); 1193 1194 1195 dialog = gtk_file_chooser_dialog_new (title, 1196 GTK_WINDOW (view->mainwin), 1197 action, //GTK_FILE_CHOOSER_ACTION_OPEN, 1198 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 1199 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, 1200 NULL); 1201 1202 while (TRUE) { 1203 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { 1204 filename = gtk_file_chooser_get_filename ( 1205 GTK_FILE_CHOOSER (dialog)); 1206 1207 if (filename == NULL) 1208 continue; 1209 if (no_check) /* don't need any check so break */ 1210 break; 1211 1212 if (action == GTK_FILE_CHOOSER_ACTION_OPEN) { /* Open REG file */ 1213 struct stat statbuf; 1214 1215 stat (filename, &statbuf); 1216 if (statbuf.st_size != 0) { 1217 if (! fsexam_gui_show_yesno_dialog (GTK_WINDOW (dialog), 1218 _(emp_msg))) { 1219 g_free (filename); 1220 filename = NULL; 1221 continue; 1222 } 1223 } 1224 } 1225 1226 if ((ensure_utf8) && ! g_utf8_validate (filename, -1, NULL)) { 1227 if (fsexam_gui_show_yesno_dialog ( 1228 GTK_WINDOW (dialog), _(nonutf8_msg))) { 1229 gboolean ret; 1230 gchar *result = NULL; 1231 1232 ret = fsexam_convert_single_filename (view->setting, 1233 filename, 1234 &result); 1235 if (ret) { /* convert success */ 1236 g_free (filename); 1237 filename = result; 1238 break; 1239 }else{ 1240 gchar *uri; 1241 GtkWidget *dlg; 1242 1243 if (fsexam_errno == ERR_CANCEL_CONVERSION) { 1244 g_free (filename); 1245 filename = NULL; 1246 break; 1247 } 1248 1249 uri = g_filename_to_uri (filename, NULL, NULL); 1250 dlg = gtk_message_dialog_new ( 1251 GTK_WINDOW (dialog), 1252 GTK_DIALOG_DESTROY_WITH_PARENT, 1253 GTK_MESSAGE_ERROR, 1254 GTK_BUTTONS_CLOSE, 1255 _(converr_msg), 1256 uri, 1257 fsexam_error_get_msg ()); 1258 g_signal_connect (G_OBJECT (dlg), "response", 1259 G_CALLBACK (gtk_widget_destroy), 1260 NULL); 1261 1262 gtk_dialog_run (GTK_DIALOG (dlg)); 1263 1264 gtk_widget_destroy (dlg); 1265 g_free (filename); 1266 g_free (uri); 1267 filename = NULL; 1268 continue; 1269 } 1270 }else{ /* fsexam_gui_show_yesno_dialog (...) */ 1271 break; 1272 } 1273 }else{ /* if ((ensure_utf8) && !g_utf8_validate (...) */ 1274 break; 1275 } 1276 }else{ /* if (GTK_DIALOG_RUN (dialog) == GTK_RESPONSE_ACCEPT) */ 1277 g_free (filename); 1278 filename = NULL; 1279 break; 1280 } 1281 } 1282 1283 gtk_widget_destroy (dialog); 1284 1285 return filename; 1286 } 1287 1288 /* 1289 * If dir is symlink or under symlink directory, convert it 1290 * to absolute path firstly. 1291 */ 1292 void 1293 fsexam_change_dir (const gchar *dir) 1294 { 1295 if (dir == NULL) 1296 return; 1297 1298 if (view->rootdir != NULL && strcmp (dir, view->rootdir) == 0) 1299 return; 1300 1301 if (! fsexam_treeview_construct (dir)) 1302 return; 1303 1304 fsexam_undo_removeall (); /* clear undo stack */ 1305 fsexam_statusbar_fresh (); /* refresh the status bar msg stack */ 1306 1307 return; 1308 } 1309 1310 /* callback for open menu */ 1311 void 1312 fsexam_choose_dir () 1313 { 1314 gchar *filename = NULL; 1315 1316 filename = fsexam_file_chooser_get_name (_("Folder Selection"), 1317 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, 1318 FALSE, 1319 TRUE); 1320 fsexam_change_dir (filename); 1321 g_free (filename); 1322 1323 return; 1324 } 1325 1326 void 1327 show_help() 1328 { 1329 GError *err = NULL; 1330 1331 gnome_help_display ("fsexam.xml", "fsexam-intro", &err); 1332 1333 if (err) { 1334 GtkWidget *dialog; 1335 dialog = gtk_message_dialog_new ( 1336 GTK_WINDOW (view->mainwin), 1337 GTK_DIALOG_DESTROY_WITH_PARENT, 1338 GTK_MESSAGE_ERROR, 1339 GTK_BUTTONS_CLOSE, 1340 _("There was an error when displaying help: %s"), 1341 err->message); 1342 1343 g_signal_connect (G_OBJECT (dialog), 1344 "response", 1345 G_CALLBACK (gtk_widget_destroy), 1346 NULL); 1347 1348 gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); 1349 gtk_widget_show (dialog); 1350 g_error_free (err); 1351 } 1352 1353 return; 1354 } 1355 1356 void 1357 fsexam_about () 1358 { 1359 static GtkWidget *about = NULL; 1360 1361 gchar *authors [] = { 1362 "Yandong Yao <Yandong.Yao (at) Sun.COM>", 1363 "Federic Zhang <Federic.Zhang (at) Sun.COM>", 1364 "Yong Sun <Yong.Sun (at) Sun.COM>", 1365 NULL 1366 }; 1367 1368 if (about != NULL) { 1369 gtk_window_present (GTK_WINDOW (about)); 1370 return; 1371 } 1372 1373 about = gnome_about_new (("fsexam"), VERSION, 1374 "Copyright 2003-2008", 1375 _("fsexam is to help migrate file name and file content from" 1376 " legacy encoding to UTF8"), 1377 (const gchar **)authors, 1378 NULL, NULL, NULL); 1379 1380 gtk_window_set_destroy_with_parent (GTK_WINDOW (about), TRUE); 1381 1382 g_signal_connect (G_OBJECT (about), "destroy", 1383 G_CALLBACK (gtk_widget_destroyed), &about); 1384 1385 gtk_widget_show (about); 1386 1387 return; 1388 } 1389 1390 /* construct GtkTreeView column and renderer */ 1391 static void 1392 common_construct_ui () 1393 { 1394 static gboolean initialized = FALSE; 1395 GtkCellRenderer *renderer; 1396 GtkTreeViewColumn *column; 1397 GtkWidget *treeview; 1398 1399 if (initialized) 1400 return; 1401 1402 initialized = TRUE; 1403 1404 /* construct tree view */ 1405 treeview = g_object_get_data (G_OBJECT (view->mainwin), "treeview_file"); 1406 1407 /* ICON_COLUMN + DISPLAYNAME_COLUMN */ 1408 column = gtk_tree_view_column_new (); 1409 1410 /* Icon renderer */ 1411 renderer = gtk_cell_renderer_pixbuf_new (); 1412 gtk_tree_view_column_pack_start (column, renderer, FALSE); 1413 gtk_tree_view_column_set_attributes (column, renderer, 1414 "pixbuf", ICON_COLUMN, 1415 NULL); 1416 1417 /* text renderer */ 1418 renderer = gtk_cell_renderer_text_new (); 1419 gtk_tree_view_column_pack_start (column, renderer, TRUE); 1420 gtk_tree_view_column_set_attributes (column, renderer, 1421 "text", DISPLAYNAME_COLUMN, 1422 NULL); 1423 1424 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); 1425 gtk_tree_view_column_set_resizable (column, TRUE); 1426 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE); 1427 1428 // gtk_tree_view_column_set_sort_column_id (column, FILENAME_COLUMN); 1429 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); 1430 1431 /* FILENAME_COLUMN */ 1432 column = gtk_tree_view_column_new (); 1433 1434 renderer = gtk_cell_renderer_text_new (); 1435 gtk_tree_view_column_pack_start (column, renderer, TRUE); 1436 gtk_tree_view_column_set_attributes (column, renderer, 1437 "text", FILENAME_COLUMN, 1438 NULL); 1439 1440 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); 1441 gtk_tree_view_column_set_resizable (column, TRUE); 1442 gtk_tree_view_column_set_visible (column, FALSE); 1443 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE); 1444 1445 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); 1446 1447 /* set GtkTreeView selection mode */ 1448 gtk_tree_selection_set_mode ( 1449 gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), 1450 GTK_SELECTION_MULTIPLE); 1451 1452 return; 1453 } 1454 1455 /* 1456 * Construct file list in the left pane and display dirname in location 1457 * field. 1458 */ 1459 void 1460 fsexam_construct_ui (const gchar *dir) 1461 { 1462 if (NULL == dir) 1463 return; 1464 1465 common_construct_ui (); /* create GtkTreeView */ 1466 fsexam_treeview_construct (dir); /* create GtkTreeModel */ 1467 1468 return; 1469 } 1470 1471 /* 1472 * Create and show search pane 1473 */ 1474 static void 1475 create_search_treeview_column (GtkTreeView *treeview) 1476 { 1477 GtkTreeViewColumn *column = NULL; 1478 GtkCellRenderer *renderer = NULL; 1479 GtkListStore *model = NULL; 1480 1481 /* Create empty list store */ 1482 model = gtk_list_store_new (3, 1483 GDK_TYPE_PIXBUF, 1484 G_TYPE_STRING, 1485 G_TYPE_STRING); 1486 gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (model)); 1487 g_object_unref (model); 1488 1489 //gtk_tree_view_set_headers_visible (treeview, TRUE); 1490 gtk_tree_selection_set_mode ( 1491 gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), 1492 GTK_SELECTION_MULTIPLE); 1493 1494 /* display name column */ 1495 column = gtk_tree_view_column_new (); 1496 //gtk_tree_view_column_set_title (column, _("Name")); 1497 1498 renderer = gtk_cell_renderer_pixbuf_new (); 1499 gtk_tree_view_column_pack_start (column, renderer, FALSE); 1500 gtk_tree_view_column_set_attributes (column, 1501 renderer, "pixbuf", 1502 ICON_COLUMN, NULL); 1503 1504 renderer = gtk_cell_renderer_text_new (); 1505 gtk_tree_view_column_pack_start (column, renderer, TRUE); 1506 gtk_tree_view_column_set_attributes (column, 1507 renderer, "text", 1508 DISPLAYNAME_COLUMN, 1509 NULL); 1510 1511 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); 1512 gtk_tree_view_column_set_resizable (column, TRUE); 1513 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE); 1514 //gtk_tree_view_column_set_sort_column_id (column, DISPLAYNAME_COLUMN); 1515 1516 gtk_tree_view_append_column (treeview, column); 1517 1518 /* real name column */ 1519 column = gtk_tree_view_column_new (); 1520 1521 renderer = gtk_cell_renderer_text_new (); 1522 gtk_tree_view_column_pack_start (column, renderer, TRUE); 1523 gtk_tree_view_column_set_attributes (column, 1524 renderer, "text", 1525 FILENAME_COLUMN, 1526 NULL); 1527 1528 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); 1529 gtk_tree_view_column_set_resizable (column, TRUE); 1530 gtk_tree_view_column_set_visible (column, FALSE); 1531 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE); 1532 1533 gtk_tree_view_append_column (treeview, column); 1534 1535 /* connect signals */ 1536 gtk_widget_add_events (GTK_WIDGET (treeview), GDK_BUTTON_PRESS_MASK); 1537 g_signal_connect (G_OBJECT (treeview), "button_press_event", 1538 G_CALLBACK (button_press), NULL); 1539 g_signal_connect (G_OBJECT (treeview), "button_release_event", 1540 G_CALLBACK (button_release), NULL); 1541 g_signal_connect (G_OBJECT (treeview), "key_press_event", 1542 G_CALLBACK (key_press), NULL); 1543 1544 return; 1545 } 1546 1547 void 1548 fsexam_search_treeview_show () 1549 { 1550 GtkWidget *treeview = NULL; 1551 GtkWidget *widget = NULL; 1552 1553 treeview = g_object_get_data (G_OBJECT (view->mainwin), "treeview_search"); 1554 1555 if (gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)) == NULL) 1556 create_search_treeview_column (GTK_TREE_VIEW (treeview)); 1557 1558 /* show the search result pane for the first time */ 1559 widget = g_object_get_data (G_OBJECT (view->mainwin), "vpaned_main"); 1560 gtk_widget_show (gtk_paned_get_child2 (GTK_PANED (widget))); 1561 gtk_paned_set_position (GTK_PANED (widget), 1562 2 * widget->allocation.height / 3); 1563 1564 /* check search result menu */ 1565 widget = g_object_get_data (G_OBJECT (view->mainwin), "menu_search_result"); 1566 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (widget), TRUE); 1567 1568 return; 1569 } 1570 1571 void 1572 fsexam_search_treeview_hide () 1573 { 1574 GtkWidget *vpaned_main = NULL; 1575 GtkWidget *widget = NULL; 1576 1577 vpaned_main = g_object_get_data (G_OBJECT (view->mainwin), "vpaned_main"); 1578 gtk_paned_set_position (GTK_PANED (vpaned_main), 1579 vpaned_main->allocation.height); 1580 1581 /* check search result menu */ 1582 widget = g_object_get_data (G_OBJECT (view->mainwin), "menu_search_result"); 1583 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (widget), FALSE); 1584 1585 return; 1586 } 1587 1588 /* 1589 * Append search result into search result listview 1590 * 1591 * If filename == NULL, then show result pane and clear old result only 1592 */ 1593 void 1594 fsexam_search_treeview_append_file (const gchar *filename, gboolean first_file) 1595 { 1596 GtkTreeView *treeview = NULL; 1597 GtkListStore *model = NULL; 1598 GtkTreeIter iter; 1599 gchar *abs_path = NULL; 1600 gchar *display_name = NULL; 1601 gchar *internal_name = NULL; 1602 gboolean showned; 1603 1604 showned = gtk_check_menu_item_get_active ( 1605 g_object_get_data (G_OBJECT (view->mainwin), "menu_search_result")); 1606 1607 if (!showned) /* show search result treeview if not */ 1608 fsexam_search_treeview_show (); 1609 1610 treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT (view->mainwin), 1611 "treeview_search")); 1612 model = GTK_LIST_STORE (gtk_tree_view_get_model (treeview)); 1613 1614 if (first_file) { 1615 gtk_list_store_clear (model); 1616 } 1617 1618 if (filename == NULL) 1619 return; 1620 1621 abs_path = get_abs_path (filename); 1622 1623 if (abs_path == NULL) { 1624 fsexam_gui_display_msg (filename, 1625 g_file_test (filename, G_FILE_TEST_IS_SYMLINK) ? 1626 _("Symlink target doesn't exist") : 1627 _("doesn't exist")); 1628 return; 1629 } 1630 1631 /* add search result into search tree view */ 1632 display_name = fsexam_filename_display_name (abs_path); 1633 internal_name = g_strescape (abs_path, ""); 1634 gtk_list_store_append(model, &iter); 1635 gtk_list_store_set (model, &iter, 1636 ICON_COLUMN, get_file_pixbuf (abs_path), 1637 DISPLAYNAME_COLUMN, display_name, 1638 FILENAME_COLUMN, internal_name, 1639 -1); 1640 1641 g_free (abs_path); 1642 g_free (display_name); 1643 g_free (internal_name); 1644 1645 return; 1646 } 1647 1648 void 1649 fsexam_search_treeview_append_list (GList *list) 1650 { 1651 GtkWidget *notebook = NULL; 1652 gboolean first_file = TRUE; 1653 1654 if (list == NULL) 1655 return; 1656 1657 while (list) { 1658 fsexam_search_treeview_append_file (list->data, first_file); 1659 1660 first_file = FALSE; 1661 list = list->next; 1662 } 1663 1664 /* show conversion log notebook page */ 1665 notebook = g_object_get_data (G_OBJECT (view->mainwin), "notebook_report"); 1666 gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), 0); 1667 1668 return; 1669 } 1670 1671 gboolean 1672 fsexam_gui_show_yesno_dialog (GtkWindow *parent, const gchar *msg_format, ...) 1673 { 1674 GtkWidget *dialog = NULL; 1675 gchar *msg = NULL; 1676 va_list args; 1677 gboolean ret = FALSE; 1678 1679 if (msg_format) { 1680 va_start (args, msg_format); 1681 msg = g_strdup_vprintf (msg_format, args); 1682 va_end (args); 1683 } 1684 1685 dialog = gtk_message_dialog_new ( 1686 parent, 1687 GTK_DIALOG_DESTROY_WITH_PARENT, 1688 GTK_MESSAGE_QUESTION, 1689 GTK_BUTTONS_YES_NO, 1690 msg); 1691 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES) { 1692 ret = TRUE; 1693 } 1694 1695 g_free (msg); 1696 1697 gtk_widget_destroy (dialog); 1698 1699 return ret; 1700 } 1701 1702 void 1703 fsexam_gui_show_dialog (GtkWindow *parent, 1704 GtkMessageType type, 1705 const gchar *message_format, 1706 ...) 1707 { 1708 GtkWidget *dialog = NULL; 1709 gchar *msg = NULL; 1710 va_list args; 1711 1712 if (message_format) { 1713 va_start (args, message_format); 1714 msg = g_strdup_vprintf (message_format, args); 1715 va_end (args); 1716 } 1717 1718 dialog = gtk_message_dialog_new ( 1719 parent, 1720 GTK_DIALOG_DESTROY_WITH_PARENT, 1721 type, 1722 GTK_BUTTONS_CLOSE, 1723 msg); 1724 1725 g_signal_connect (G_OBJECT (dialog), "response", 1726 G_CALLBACK (gtk_widget_destroy), NULL); 1727 1728 gtk_dialog_run (GTK_DIALOG (dialog)); 1729 1730 g_free (msg); 1731 1732 return; 1733 } 1734 1735 /* 1736 * Display msg into report pane. 1737 * filename: maybe non-utf8 1738 * msg: must be utf8 1739 */ 1740 void 1741 fsexam_gui_display_msg (const gchar *filename, const gchar *msg) 1742 { 1743 GtkWidget *textview = NULL; 1744 GtkTextBuffer *buffer = NULL; 1745 GtkTextMark *mark = NULL; 1746 GtkTextIter iter; 1747 gchar *messages = NULL; 1748 static gboolean created_mark = FALSE; 1749 static gboolean tag_created = FALSE; 1750 1751 textview = g_object_get_data (G_OBJECT (view->mainwin), 1752 "textview_report"); 1753 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview)); 1754 gtk_text_buffer_get_end_iter (buffer, &iter); 1755 1756 /* creat mark based on iter */ 1757 if (!created_mark) { 1758 mark = gtk_text_buffer_create_mark (buffer, "end_mark", &iter, FALSE); 1759 created_mark = TRUE; 1760 }else{ 1761 mark = gtk_text_buffer_get_mark (buffer, "end_mark"); 1762 } 1763 1764 /* create BOLD tag */ 1765 if (!tag_created) { 1766 gtk_text_buffer_create_tag (buffer, 1767 "BOLD_TAG", 1768 "weight", PANGO_WEIGHT_BOLD, 1769 NULL); 1770 tag_created = TRUE; 1771 } 1772 1773 if (filename) { 1774 gchar *dname = g_path_get_dirname (filename); 1775 gchar *bname = g_path_get_basename (filename); 1776 gchar *prev_line = NULL; 1777 1778 /* Under the same directory as the previous report? */ 1779 prev_line = get_last_path (buffer, &iter); 1780 if (prev_line == NULL || strcmp (dname, prev_line) != 0) { 1781 gtk_text_buffer_get_end_iter (buffer, &iter); 1782 1783 if (g_utf8_validate (dname, -1, NULL)) { 1784 messages = g_strdup_printf ("%s\n", dname); 1785 }else{ 1786 gchar *uri = g_filename_to_uri (dname, NULL, NULL); 1787 messages = g_strdup_printf ("%s\n", uri); 1788 g_free (uri); 1789 } 1790 1791 gtk_text_buffer_insert (buffer, &iter, messages, -1); 1792 g_free (messages); 1793 } 1794 1795 /* print basename and conversion message */ 1796 gtk_text_buffer_get_end_iter (buffer, &iter); 1797 gtk_text_buffer_insert (buffer, &iter, "\t\t", -1); 1798 1799 gtk_text_buffer_get_end_iter (buffer, &iter); 1800 if (g_utf8_validate (bname, -1, NULL)) { 1801 gtk_text_buffer_insert_with_tags_by_name (buffer, 1802 &iter, 1803 bname, 1804 -1, 1805 "BOLD_TAG", 1806 NULL); 1807 }else{ 1808 gchar *escape = fsexam_string_escape (bname); 1809 gtk_text_buffer_insert_with_tags_by_name (buffer, 1810 &iter, 1811 escape, 1812 -1, 1813 "BOLD_TAG", 1814 NULL); 1815 1816 g_free (escape); 1817 } 1818 1819 gtk_text_buffer_get_end_iter (buffer, &iter); 1820 messages = g_strdup_printf (":\t%s\n", msg ? msg : ""); 1821 gtk_text_buffer_insert (buffer, &iter, messages, -1); 1822 g_free (messages); 1823 1824 g_free (dname); 1825 g_free (bname); 1826 g_free (prev_line); 1827 }else{ 1828 gtk_text_buffer_insert (buffer, &iter, msg, -1); 1829 gtk_text_buffer_get_end_iter (buffer, &iter); 1830 gtk_text_buffer_insert (buffer, &iter, "\n", -1); 1831 } 1832 1833 /* scroll text view so that the newest line is visible */ 1834 gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (textview), 1835 mark, 1836 0.0, 1837 TRUE, 1838 0.0, 1839 1.0); 1840 1841 return; 1842 } 1843 1844 /* 1845 * callbacks for FSEXAM_setting functions 1846 * 1847 * update TreeView after convert file name successfully. 1848 */ 1849 void 1850 fsexam_gui_update ( FSEXAM_setting *setting, 1851 const gchar *path, 1852 const gchar *oldname, 1853 const gchar *newname) 1854 { 1855 GtkWidget *tree_view = NULL; 1856 GtkTreeModel *tree_model = NULL; 1857 GtkTreeModel *another_model = NULL; 1858 GtkTreePath *tree_path = NULL; 1859 gchar *tree_path_str = NULL; 1860 gchar *old_full_name = NULL; 1861 gchar *new_full_name = NULL; 1862 gboolean on_treeview_search = FALSE; 1863 1864 if (setting->pref->conv_content) 1865 return; 1866 1867 if ((NULL == path) || (NULL == oldname) 1868 || (NULL == newname) || (view->focus_treeview == NULL)) 1869 return; 1870 1871 tree_view = view->focus_treeview; 1872 tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)); 1873 1874 if (strcmp (gtk_widget_get_name (tree_view), "treeview_search") == 0) 1875 on_treeview_search = TRUE; 1876 1877 tree_view = g_object_get_data (G_OBJECT (view->mainwin), 1878 on_treeview_search ? "treeview_file" : "treeview_search"); 1879 another_model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)); 1880 1881 old_full_name = g_strdup_printf ("%s/%s", path, oldname); 1882 new_full_name = g_strdup_printf ("%s/%s", path, newname); 1883 1884 /* lookup hash to get the GtkTreePath string */ 1885 if (view->treepath_hash != NULL) { 1886 tree_path_str = (gchar *)g_hash_table_lookup (view->treepath_hash, 1887 old_full_name); 1888 } 1889 1890 if (tree_path_str != NULL) 1891 tree_path = gtk_tree_path_new_from_string (tree_path_str); 1892 1893 /* update search result treeview */ 1894 update_gui_for_filelist (on_treeview_search ? tree_model : another_model, 1895 old_full_name, 1896 new_full_name, 1897 tree_path, 1898 on_treeview_search ? TRUE : FALSE); 1899 1900 /* update folder treeview */ 1901 update_gui_for_dir (on_treeview_search ? another_model : tree_model, 1902 path, 1903 oldname, 1904 newname, 1905 tree_path, 1906 on_treeview_search ? FALSE : TRUE); 1907 1908 g_free (new_full_name); 1909 g_free (old_full_name); 1910 gtk_tree_path_free (tree_path); 1911 1912 return; 1913 } 1914 1915 extern gint indexg; 1916 1917 gint 1918 fsexam_gui_get_index (GList *encoding_list, gboolean forname) 1919 { 1920 GtkWidget *dialog; 1921 gint response; 1922 gboolean donnot_ask; 1923 1924 indexg = -1; /* init indexg to override previous value */ 1925 1926 dialog = fsexam_dialog_candidate (encoding_list, forname); 1927 response = gtk_dialog_run (GTK_DIALOG (dialog)); 1928 1929 donnot_ask = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON ( 1930 g_object_get_data (G_OBJECT (dialog), "chkbtn_ask"))); 1931 1932 /* now indexg should contain the real index */ 1933 if (response == GTK_RESPONSE_OK){ 1934 gint index = 0; 1935 1936 if (indexg == -1){ 1937 /* use the first available encode */ 1938 fsexam_encoding_iterate_with_func (encoding_list, 1939 fsexam_encoding_translate_index, 1940 &index, 1941 &indexg); 1942 } 1943 1944 /* indexg is the index in encoding_list, so it is safe */ 1945 if (donnot_ask) 1946 view->setting->gold_index = indexg; 1947 }else{ 1948 indexg = -1; 1949 } 1950 1951 /* indexg == -1 means cancel conversion by user */ 1952 if (indexg == -1 && donnot_ask) 1953 view->setting->flags |= FSEXAM_SETTING_FLAGS_STOP; 1954 1955 gtk_widget_destroy (dialog); 1956 1957 return indexg; 1958 } 1959 1960 #define END_LINE "==================================================\n" 1961 void 1962 fsexam_gui_display_stats (FSEXAM_setting *setting) 1963 { 1964 gchar *stats_info = NULL; 1965 GtkTextView *textview = NULL; 1966 GtkTextBuffer *buffer = NULL; 1967 GtkTextMark *mark = NULL; 1968 GtkTextIter iter, start, end; 1969 gchar *text; 1970 gint line; 1971 static gboolean tag_created = FALSE; 1972 1973 stats_info = g_strdup_printf ( 1974 _("Rough summary: %d given, %d total, %d ignore, %d fail, %d succeed\n"), 1975 setting->passin_num, 1976 setting->total_num, 1977 setting->ignore_num, 1978 setting->fail_num, 1979 setting->succ_num); 1980 1981 /* display statastics information in statusbar */ 1982 *(stats_info + strlen (stats_info) - 1) = '\0'; 1983 fsexam_statusbar_update (stats_info); 1984 g_free (stats_info); 1985 1986 /* display something in report pane to make it clear to see */ 1987 textview = g_object_get_data (G_OBJECT (view->mainwin), 1988 "textview_report"); 1989 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview)); 1990 gtk_text_buffer_get_end_iter (buffer, &iter); 1991 1992 line = gtk_text_iter_get_line (&iter) - 1; 1993 gtk_text_buffer_get_iter_at_line_offset (buffer, &start, line, 0); 1994 gtk_text_buffer_get_iter_at_line_offset (buffer, &end, line + 1, 0); 1995 text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); 1996 1997 if (text == NULL || *text == '=') { /* No new data, return */ 1998 g_free (text); 1999 return; 2000 } 2001 2002 if (!tag_created) { 2003 gtk_text_buffer_create_tag (buffer, 2004 "SUMMARY_TAG", /* tag name */ 2005 "foreground", "blue", 2006 //"weight", PANGO_WEIGHT_BOLD, 2007 NULL); 2008 tag_created = TRUE; 2009 } 2010 2011 mark = gtk_text_buffer_get_mark (buffer, "end_mark"); 2012 2013 if (mark == NULL) { 2014 mark = gtk_text_buffer_create_mark (buffer, "end_mark", &iter, FALSE); 2015 } 2016 2017 gtk_text_buffer_insert_with_tags_by_name (buffer, 2018 &iter, 2019 END_LINE, 2020 -1, 2021 "SUMMARY_TAG", 2022 NULL); 2023 2024 gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (textview), mark); 2025 2026 g_free (text); 2027 2028 return; 2029 } 2030 2031 /* 2032 * Load Glade xml file 2033 */ 2034 GladeXML * 2035 fsexam_gui_load_glade_file (const gchar *filename, 2036 const gchar *widget_root, 2037 GtkWindow *error_dialog_parent) 2038 { 2039 gchar *path = NULL; 2040 GladeXML *xml = NULL; 2041 2042 path = g_strconcat ("./", filename, NULL); /* for test in local dir */ 2043 if (g_file_test (path, G_FILE_TEST_EXISTS)) { 2044 xml = glade_xml_new (path, widget_root, GETTEXT_PACKAGE); 2045 } 2046 2047 if (xml == NULL) { 2048 g_free (path); 2049 path = g_build_filename (FSEXAM_GLADE_DIR, filename, NULL); 2050 2051 xml = glade_xml_new (path, widget_root, GETTEXT_PACKAGE); 2052 } 2053 2054 if (xml == NULL) { 2055 fsexam_gui_show_dialog (error_dialog_parent, 2056 GTK_MESSAGE_ERROR, 2057 _("The file \"%s\" is missing."), 2058 path); 2059 }else{ 2060 glade_xml_signal_autoconnect (xml); 2061 } 2062 2063 g_free (path); 2064 2065 return xml; 2066 } 2067 2068 FSEXAM_view * 2069 fsexam_view_new () 2070 { 2071 FSEXAM_view *view = NULL; 2072 GladeXML *xml = NULL; 2073 GtkWidget *mainwin = NULL; 2074 GtkWidget *w = NULL; 2075 gchar *icon_path = NULL; 2076 2077 view = g_new0 (FSEXAM_view, 1); 2078 2079 view->pixbuf_hash = g_hash_table_new_full ( 2080 g_str_hash, 2081 g_str_equal, 2082 (GDestroyNotify) g_free, 2083 (GDestroyNotify) g_object_unref); 2084 view->treepath_hash = g_hash_table_new_full ( 2085 g_str_hash, 2086 g_str_equal, 2087 (GDestroyNotify) g_free, 2088 (GDestroyNotify) g_free); 2089 view->undo_list = NULL; 2090 view->rootdir = NULL; 2091 view->basedir = NULL; 2092 view->lineoffset = 0; 2093 view->pid = -1; 2094 2095 view->peekwin = NULL; 2096 view->focus_treeview = NULL; 2097 2098 /* set the default icon for all window */ 2099 icon_path = g_strdup_printf ("%s/%s", FSEXAM_ICON_DIR, FSEXAM_ICON_FILE); 2100 gtk_window_set_default_icon_from_file (icon_path, NULL); 2101 g_free (icon_path); 2102 2103 /* Main Window */ 2104 xml = fsexam_gui_load_glade_file ( 2105 FSEXAM_GLADE_FILE, 2106 "app_window", 2107 NULL); 2108 if (xml == NULL) 2109 return view; 2110 2111 mainwin = view->mainwin = glade_xml_get_widget (xml, "app_window"); 2112 g_signal_connect (G_OBJECT (mainwin), "destroy", 2113 G_CALLBACK (mainwin_destroy), NULL); 2114 g_signal_connect (G_OBJECT (mainwin), "delete-event", 2115 G_CALLBACK (mainwin_delete), NULL); 2116 2117 fsexam_dnd_set (mainwin); /* set DnD */ 2118 2119 /* main menu */ 2120 w = glade_xml_get_widget(xml, "menu_quit"); 2121 g_signal_connect (G_OBJECT (w), "activate", 2122 G_CALLBACK (mainwin_destroy), NULL); 2123 2124 /* toolbar */ 2125 w = glade_xml_get_widget (xml, "toolbar_main"); 2126 g_object_set_data (G_OBJECT (mainwin), "toolbar", w); 2127 2128 /* Treeview and its mouse and button events */ 2129 w = glade_xml_get_widget (xml, "treeview_file"); 2130 g_object_set_data (G_OBJECT (mainwin), "treeview_file", w); 2131 gtk_widget_add_events (w, GDK_BUTTON_PRESS_MASK); 2132 g_signal_connect (GTK_TREE_VIEW (w), "button_press_event", 2133 G_CALLBACK (button_press), NULL); 2134 g_signal_connect (GTK_TREE_VIEW (w), "button_release_event", 2135 G_CALLBACK (button_release), NULL); 2136 g_signal_connect (GTK_TREE_VIEW (w), "key_press_event", 2137 G_CALLBACK (key_press), NULL); 2138 2139 /* status bar */ 2140 w = glade_xml_get_widget (xml, "appbar1"); 2141 g_object_set_data (G_OBJECT (mainwin), "appbar", w); 2142 2143 /* notebook */ 2144 w = glade_xml_get_widget (xml, "notebook_report"); 2145 g_object_set_data (G_OBJECT (mainwin), "notebook_report", w); 2146 2147 w = glade_xml_get_widget (xml, "scrollwin_report"); 2148 g_object_set_data (G_OBJECT (mainwin), "scrollwin_report", w); 2149 2150 w = glade_xml_get_widget (xml, "textview_report"); 2151 g_object_set_data (G_OBJECT (mainwin), "textview_report", w); 2152 2153 w = glade_xml_get_widget (xml, "scrollwin_dryrun"); 2154 g_object_set_data (G_OBJECT (mainwin), "scrollwin_dryrun", w); 2155 2156 w = glade_xml_get_widget (xml, "textview_dryrun"); 2157 g_object_set_data (G_OBJECT (mainwin), "textview_dryrun", w); 2158 2159 /* folder entry */ 2160 w = glade_xml_get_widget (xml, "entry_folder"); 2161 g_object_set_data (G_OBJECT (mainwin), "entry_folder", w); 2162 2163 /* submenu */ 2164 w = glade_xml_get_widget (xml, "menu_search"); 2165 g_object_set_data (G_OBJECT (mainwin), "menu_search", w); 2166 2167 w = glade_xml_get_widget (xml, "menu_stop_search"); 2168 g_object_set_data (G_OBJECT (mainwin), "menu_stop_search", w); 2169 2170 w = glade_xml_get_widget (xml, "undo_menu"); 2171 g_object_set_data (G_OBJECT (mainwin), "menu_undo", w); 2172 gtk_widget_set_sensitive (w, FALSE); 2173 2174 w = glade_xml_get_widget (xml, "convert_menu"); 2175 g_object_set_data (G_OBJECT (mainwin), "menu_convert", w); 2176 2177 w = glade_xml_get_widget (xml, "restore_menu"); 2178 g_object_set_data (G_OBJECT (mainwin), "menu_restore", w); 2179 2180 w = glade_xml_get_widget (xml, "dryrun_menu"); 2181 g_object_set_data (G_OBJECT (mainwin), "menu_dryrun", w); 2182 2183 w = glade_xml_get_widget (xml, "menu_scenario"); 2184 g_object_set_data (G_OBJECT (mainwin), "menu_scenario", w); 2185 2186 w = glade_xml_get_widget (xml, "force_convert_menu"); 2187 g_object_set_data (G_OBJECT (mainwin), "menu_force_convert", w); 2188 2189 w = glade_xml_get_widget (xml, "menu_clear_search"); 2190 g_object_set_data (G_OBJECT (mainwin), "menu_clear_search", w); 2191 2192 w = glade_xml_get_widget (xml, "name_mode_menu"); 2193 g_object_set_data (G_OBJECT (mainwin), "menu_name_mode", w); 2194 2195 w = glade_xml_get_widget (xml, "content_mode_menu"); 2196 g_object_set_data (G_OBJECT (mainwin), "menu_content_mode", w); 2197 2198 w = glade_xml_get_widget (xml, "report_pane_menu"); 2199 g_object_set_data (G_OBJECT (mainwin), "menu_report", w); 2200 2201 w = glade_xml_get_widget (xml, "dryrun_result_menu"); /* dryrun result under view */ 2202 g_object_set_data (G_OBJECT (mainwin), "menu_dryrun_result", w); 2203 2204 w = glade_xml_get_widget (xml, "menu_search_result"); 2205 g_object_set_data (G_OBJECT (mainwin), "menu_search_result", w); 2206 2207 /* toolbar button */ 2208 w = glade_xml_get_widget (xml, "toolbutton_convert"); 2209 g_object_set_data (G_OBJECT (mainwin), "toolbutton_convert", w); 2210 2211 w = glade_xml_get_widget (xml, "toolbutton_restore"); 2212 g_object_set_data (G_OBJECT (mainwin), "toolbutton_restore", w); 2213 2214 w = glade_xml_get_widget (xml, "toolbutton_dryrun"); 2215 g_object_set_data (G_OBJECT (mainwin), "toolbutton_dryrun", w); 2216 2217 w = glade_xml_get_widget (xml, "toolbutton_force"); 2218 g_object_set_data (G_OBJECT (mainwin), "toolbutton_force", w); 2219 2220 w = glade_xml_get_widget (xml, "toolbutton_undo"); 2221 g_object_set_data (G_OBJECT (mainwin), "toolbutton_undo", w); 2222 2223 w = glade_xml_get_widget (xml, "toolbutton_scenario"); 2224 g_object_set_data (G_OBJECT (mainwin), "toolbutton_scenario", w); 2225 2226 /* search result pane */ 2227 w = glade_xml_get_widget (xml, "vpaned_main"); 2228 g_object_set_data (G_OBJECT (mainwin), "vpaned_main", w); 2229 2230 w = glade_xml_get_widget (xml, "label_result"); 2231 g_object_set_data (G_OBJECT (mainwin), "label_result", w); 2232 2233 w = glade_xml_get_widget (xml, "treeview_search"); 2234 g_object_set_data (G_OBJECT (mainwin), "treeview_search", w); 2235 2236 g_object_unref (G_OBJECT (xml)); 2237 2238 /* popup menu */ 2239 xml = fsexam_gui_load_glade_file (FSEXAM_GLADE_FILE, 2240 "menu_popup", 2241 GTK_WINDOW (view->mainwin)); 2242 if (xml == NULL) 2243 return view; 2244 2245 view->popup_menu = glade_xml_get_widget (xml, "menu_popup"); 2246 #ifdef HAVE_NO_GLIB_2_8 2247 g_object_ref (view->popup_menu); 2248 #else 2249 g_object_ref_sink (view->popup_menu); /* we own this object */ 2250 #endif 2251 2252 w = glade_xml_get_widget (xml, "popup_undo"); 2253 g_object_set_data (G_OBJECT (view->popup_menu), "popup_undo", w); 2254 2255 w = glade_xml_get_widget (xml, "popup_convert"); 2256 g_object_set_data (G_OBJECT (view->popup_menu), "popup_convert", w); 2257 2258 w = glade_xml_get_widget (xml, "popup_restore"); 2259 g_object_set_data (G_OBJECT (view->popup_menu), "popup_restore", w); 2260 2261 w = glade_xml_get_widget (xml, "popup_dryrun"); 2262 g_object_set_data (G_OBJECT (view->popup_menu), "popup_dryrun", w); 2263 2264 w = glade_xml_get_widget (xml, "popup_scenario"); 2265 g_object_set_data (G_OBJECT (view->popup_menu), "popup_scenario", w); 2266 2267 w = glade_xml_get_widget (xml, "popup_forceful"); 2268 g_object_set_data (G_OBJECT (view->popup_menu), "popup_forceful", w); 2269 2270 w = glade_xml_get_widget (xml, "popup_name_mode"); 2271 g_object_set_data (G_OBJECT (view->popup_menu), "popup_name_mode", w); 2272 2273 w = glade_xml_get_widget (xml, "popup_content_mode"); 2274 g_object_set_data (G_OBJECT (view->popup_menu), "popup_content_mode", w); 2275 2276 g_object_unref (G_OBJECT (xml)); 2277 2278 return view; 2279 } 2280 2281 void 2282 fsexam_view_destroy (FSEXAM_view *view) 2283 { 2284 /* hash table */ 2285 g_hash_table_destroy (view->pixbuf_hash); 2286 g_hash_table_destroy (view->treepath_hash); 2287 view->treepath_hash = NULL; 2288 view->pixbuf_hash = NULL; 2289 2290 /* FSEXAM_setting */ 2291 if (view->setting) 2292 fsexam_setting_destroy (view->setting); 2293 view->setting = NULL; 2294 2295 /* undo list */ 2296 if (view->undo_list) { 2297 g_slist_free (view->undo_list); 2298 } 2299 view->undo_list = NULL; 2300 2301 /* popup menu object */ 2302 g_object_unref (G_OBJECT (view->popup_menu)); 2303 view->popup_menu = NULL; 2304 2305 /* dirname */ 2306 g_free (view->rootdir); 2307 view->rootdir = NULL; 2308 g_free (view->basedir); 2309 view->basedir = NULL; 2310 2311 g_free (view); 2312 2313 return; 2314 } 2315 2316 /* --------------------- Content peek funcs -------------------- */ 2317 2318 GdkPixmap *content_pixmap; 2319 2320 static void 2321 get_upper_left_xy (GtkWidget *peekwin, 2322 gint width, gint height, 2323 gint x_root, gint y_root, 2324 gint *x, gint *y) 2325 { 2326 *x = x_root; 2327 *y = y_root; 2328 2329 *x -= width; 2330 *y -= height; 2331 2332 return; 2333 } 2334 2335 2336 static void 2337 set_window_background (GtkWidget *window, GdkPixmap *pixmap) 2338 { 2339 gdk_window_set_back_pixmap (window->window, pixmap, FALSE); 2340 } 2341 2342 /* 2343 * Create a pixmap from contents 2344 */ 2345 static GdkPixmap * 2346 create_content_pixmap (GtkWidget *peekwin, gchar *content) 2347 { 2348 PangoLayout *pango_layout; 2349 PangoLayout *main_pango_layout = NULL; 2350 PangoRectangle rect; 2351 gint pixmap_width, pixmap_height; 2352 GtkStyle *style; 2353 GdkPixmap *pixmap; 2354 PangoFontDescription *font_desc; 2355 2356 enum { PADDING = 8 }; 2357 2358 /* create PangoLayout */ 2359 main_pango_layout = gtk_widget_create_pango_layout (view->mainwin, NULL); 2360 font_desc = pango_font_description_copy ( 2361 gtk_widget_get_style (view->mainwin)->font_desc); 2362 2363 pango_layout = pango_layout_new ( 2364 pango_layout_get_context (main_pango_layout)); 2365 2366 pango_layout_set_font_description (pango_layout, font_desc); 2367 2368 if (str_isutf8 (content, -1)) { 2369 pango_layout_set_text (pango_layout, content, -1); 2370 } else { 2371 gchar *display_content; 2372 2373 /* Use below function to replace non-utf8 char with '?' */ 2374 display_content = fsexam_filename_display_name (content); 2375 pango_layout_set_text (pango_layout, display_content, -1); 2376 2377 g_free (display_content); 2378 } 2379 2380 pango_layout_get_pixel_extents (pango_layout, &rect, NULL); 2381 2382 pixmap_width = rect.width + 2 * PADDING; 2383 pixmap_height = rect.height + 2 * PADDING; 2384 2385 style = gtk_widget_get_style (view->mainwin); 2386 2387 /* create pixmap */ 2388 pixmap = gdk_pixmap_new (view->mainwin->window, 2389 pixmap_width, 2390 pixmap_height, 2391 -1); 2392 2393 gdk_draw_rectangle (pixmap, 2394 style->base_gc[GTK_STATE_NORMAL], 2395 TRUE, 0, 0, 2396 pixmap_width, pixmap_height); 2397 2398 gdk_draw_rectangle (pixmap, 2399 style->fg_gc[GTK_STATE_INSENSITIVE], 2400 FALSE, 1, 1, 2401 pixmap_width - 3, pixmap_height -3); 2402 2403 gdk_draw_layout (pixmap, 2404 style->text_gc[GTK_STATE_NORMAL], 2405 -rect.x + PADDING, -rect.y + PADDING, 2406 pango_layout); 2407 2408 pango_font_description_free (font_desc); 2409 g_object_unref (pango_layout); 2410 2411 return pixmap; 2412 } 2413 2414 static void 2415 free_selection (gpointer row, gpointer data) 2416 { 2417 GtkTreePath *path = (GtkTreePath *)row; 2418 gtk_tree_path_free (path); 2419 2420 return; 2421 } 2422 2423 #define MAX_NUM_MULTIBYTE_CHARACTERS 400 2424 #define MAX_NUM_CHARACTERS 2000 2425 2426 static gchar * 2427 fsexam_content_get_sample (char *file) 2428 { 2429 gchar *p = NULL, *sample = NULL; 2430 gsize length; 2431 gint multi_num = 0, char_num = 0; 2432 GError *err = NULL; 2433 2434 if (NULL == file) 2435 return NULL; 2436 2437 if (! g_file_get_contents (file, &p, &length, &err)) { 2438 fsexam_statusbar_update (err->message); 2439 g_error_free (err); 2440 2441 return NULL; 2442 } 2443 2444 sample = p; 2445 2446 while (TRUE) { 2447 gunichar wc; 2448 2449 /* get the next Unicode character */ 2450 wc = g_utf8_get_char_validated (p, -1); 2451 if (wc == 0x0 || multi_num == MAX_NUM_MULTIBYTE_CHARACTERS 2452 || char_num == MAX_NUM_CHARACTERS) { 2453 *p = 0x0; 2454 break; 2455 } 2456 2457 if (wc == (gunichar)-1 || wc == (gunichar)-2) { /* invalid UTF8 */ 2458 ++multi_num; 2459 ++p; 2460 } else { /* valid UTF8 */ 2461 if (wc >= 0x7e) 2462 ++multi_num; 2463 2464 p = g_utf8_next_char (p); 2465 } 2466 ++char_num; 2467 } 2468 2469 return sample; 2470 } 2471 2472 /* callback for realize signal */ 2473 static void 2474 peek_window_realize (GtkWidget *peekwin, gpointer user_data) 2475 { 2476 gint width, height; 2477 2478 set_window_background (peekwin, content_pixmap); 2479 gdk_window_clear (peekwin->window); 2480 2481 gdk_drawable_get_size (GDK_DRAWABLE (content_pixmap), 2482 &width, &height); 2483 2484 gtk_widget_set_size_request (peekwin, width, height); 2485 gtk_window_resize (GTK_WINDOW (peekwin), width, height); 2486 2487 return; 2488 } 2489 2490 /* 2491 * Update peek window using new content 2492 */ 2493 static void 2494 update_peek_window (GtkWidget *peekwin, gchar *content) 2495 { 2496 gint width, height; 2497 2498 if (NULL == peekwin) 2499 return; 2500 2501 if (content_pixmap != NULL) 2502 g_object_unref (content_pixmap); 2503 2504 /* create pixmap from content */ 2505 content_pixmap = create_content_pixmap (peekwin, content); 2506 2507 if (GTK_WIDGET_REALIZED (peekwin)) { 2508 set_window_background (peekwin, content_pixmap); 2509 gdk_window_clear (peekwin->window); 2510 } 2511 2512 gdk_drawable_get_size (GDK_DRAWABLE (content_pixmap), 2513 &width, &height); 2514 2515 gtk_widget_set_size_request (peekwin, width, height); 2516 gtk_window_resize (GTK_WINDOW (peekwin), width, height); 2517 2518 return; 2519 } 2520 2521 static void 2522 place_peek_window (GtkWidget *peekwin, gint x_root, gint y_root) 2523 { 2524 gint width, height; 2525 gint x, y; 2526 2527 if (NULL == peekwin) 2528 return; 2529 2530 gtk_widget_get_size_request (peekwin, &width, &height); 2531 2532 get_upper_left_xy (peekwin, width, height, 2533 x_root, y_root, &x, &y); 2534 2535 gtk_window_move (GTK_WINDOW (peekwin), x, y); 2536 2537 return; 2538 } 2539 2540 /* 2541 * Create a toplevel peek window 2542 */ 2543 static GtkWidget * 2544 make_peek_window () 2545 { 2546 GtkWidget *peekwin = gtk_window_new (GTK_WINDOW_TOPLEVEL); 2547 2548 g_signal_connect (peekwin, "realize", 2549 G_CALLBACK (peek_window_realize), NULL); 2550 2551 gtk_window_set_type_hint (GTK_WINDOW (peekwin), 2552 GDK_WINDOW_TYPE_HINT_UTILITY); 2553 gtk_window_set_decorated (GTK_WINDOW (peekwin), FALSE); 2554 gtk_window_set_screen (GTK_WINDOW (peekwin), 2555 gtk_widget_get_screen (view->mainwin)); 2556 gtk_widget_set_app_paintable (peekwin, TRUE); 2557 2558 return peekwin; 2559 } 2560 2561 static gboolean 2562 fsexam_content_get_selection (gchar **path, gchar **filename) 2563 { 2564 GtkTreeSelection *selection; 2565 GtkTreeIter iter, subiter; 2566 GtkTreeModel *model; 2567 GtkWidget *treeview; 2568 GList *row; 2569 GString *dir; 2570 gchar *internal_name; 2571 2572 treeview = view->focus_treeview; 2573 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); 2574 row = gtk_tree_selection_get_selected_rows (selection, &model); 2575 2576 if (row == NULL) { 2577 fsexam_statusbar_update (_("No selection")); 2578 2579 return FALSE; 2580 } 2581 2582 if (g_list_length (row) > 1) { 2583 g_list_foreach (row, free_selection, NULL); 2584 g_list_free (row); 2585 2586 fsexam_statusbar_update (_("Cannot preview multiple selections")); 2587 2588 return FALSE; 2589 } 2590 2591 gtk_tree_model_get_iter (model, &iter, row->data); 2592 2593 g_list_foreach (row, free_selection, NULL); 2594 g_list_free (row); 2595 2596 if (gtk_tree_model_iter_children (model, &subiter, &iter)) { 2597 fsexam_statusbar_update (_("Cannot preview contents of folder")); 2598 2599 return FALSE; 2600 } 2601 2602 gtk_tree_model_get (model, &iter, 2603 FILENAME_COLUMN, &internal_name, 2604 -1); 2605 2606 *filename = g_strcompress (internal_name); 2607 g_free (internal_name); 2608 2609 if (**filename == '/') { /* we are on search result treeview */ 2610 *path = NULL; 2611 return TRUE; 2612 } 2613 2614 dir = fsexam_filename_get_path (model, iter); 2615 2616 *path = g_string_free (dir, FALSE); 2617 2618 return TRUE; 2619 } 2620 2621 static void 2622 peekwin_destroy (GtkWidget *widget, gpointer user_data) 2623 { 2624 gtk_object_destroy (GTK_OBJECT (view->peekwin)); 2625 view->peekwin = NULL; 2626 2627 return; 2628 } 2629 2630 void 2631 fsexam_content_peek (gint x_root, gint y_root) 2632 { 2633 gchar *path = NULL, *filename = NULL, *name = NULL; 2634 gchar *sample_text = NULL; 2635 gchar *statusmsg = NULL; 2636 gint y_root2 = y_root; 2637 struct stat statbuf; 2638 GtkWidget *widget = NULL; 2639 const gchar *widget_name; 2640 2641 widget = gtk_window_get_focus (GTK_WINDOW (view->mainwin)); 2642 widget_name = gtk_widget_get_name (widget); 2643 2644 if (strcmp (widget_name, "treeview_search") == 0) { 2645 /* convert search result */ 2646 if (view->basedir == NULL) 2647 return; 2648 2649 view->focus_treeview = widget; 2650 }else{ 2651 /* convert files in left pane */ 2652 if (NULL == view->rootdir) 2653 return; 2654 2655 widget = g_object_get_data (G_OBJECT (view->mainwin), 2656 "treeview_file"); 2657 view->focus_treeview = widget; 2658 2659 } 2660 2661 if (!fsexam_content_get_selection (&path, &filename)) 2662 return; 2663 2664 if (y_root == 0) { 2665 gint x, y, height, width; 2666 2667 gtk_window_get_position (GTK_WINDOW (view->mainwin), &x, &y); 2668 gtk_window_get_size (GTK_WINDOW (view->mainwin), &height, &width); 2669 2670 x_root = x+width/2; 2671 y_root = y+height/2; 2672 } 2673 2674 if (path == NULL) 2675 name = g_strdup (filename); 2676 else 2677 name = g_strdup_printf ("%s/%s", path, filename); 2678 2679 if (lstat (name, &statbuf) == -1) { 2680 statusmsg = g_strdup_printf ("%s: %s", _("Can't access"), filename); 2681 goto ERR; 2682 } 2683 2684 if (! S_ISREG (statbuf.st_mode)) { 2685 statusmsg = g_strdup_printf ("%s: %s", 2686 _("Not regular file"), filename); 2687 goto ERR; 2688 } 2689 2690 if (statbuf.st_size == 0) { 2691 statusmsg = g_strdup_printf ("%s: %s", _("Empty file"), filename); 2692 goto ERR; 2693 } 2694 2695 if (!fsexam_is_plain_text (name, view->setting)) { 2696 statusmsg = g_strdup_printf ("%s: %s", _("Not plain text"), filename); 2697 goto ERR; 2698 } 2699 2700 if (!view->peekwin) 2701 view->peekwin = make_peek_window (); 2702 2703 place_peek_window (view->peekwin, x_root, y_root); 2704 2705 if ((sample_text = fsexam_content_get_sample (name)) == NULL) 2706 goto ERR; 2707 2708 update_peek_window (view->peekwin, sample_text); 2709 gtk_widget_show (view->peekwin); 2710 2711 set_window_background (view->peekwin, content_pixmap); 2712 gdk_window_clear (view->peekwin->window); 2713 2714 if (y_root2 == 0) 2715 g_signal_connect (G_OBJECT (view->peekwin), "focus-in-event", 2716 G_CALLBACK (peekwin_destroy), view->peekwin); 2717 2718 g_free (sample_text); 2719 2720 ERR: 2721 if (statusmsg != NULL) { 2722 fsexam_statusbar_update (statusmsg); 2723 g_free (statusmsg); 2724 } 2725 2726 g_free (name); 2727 g_free (filename); 2728 g_free (path); 2729 2730 return; 2731 } 2732 2733 2734 GtkWidget * 2735 fsexam_gui_get_focused_treeview () 2736 { 2737 GtkWidget *widget = NULL; 2738 const gchar *name; 2739 2740 widget = gtk_window_get_focus (GTK_WINDOW (view->mainwin)); 2741 name = gtk_widget_get_name (widget); 2742 2743 if (strcmp (name, "treeview_search") != 0) { 2744 widget = g_object_get_data (G_OBJECT (view->mainwin), 2745 "treeview_file"); 2746 } 2747 2748 return widget; 2749 } 2750 2751 /* Set the initial toolbar style */ 2752 void 2753 fsexam_gui_set_initial_state (void) 2754 { 2755 GtkWidget *w = NULL; 2756 gchar *toolbar_style = NULL; 2757 2758 /* set toolbar style */ 2759 toolbar_style = gconf_client_get_string (view->setting->pref->gconf_client, 2760 TOOLBAR_STYLE, 2761 NULL); 2762 2763 if (toolbar_style != NULL) { 2764 w = g_object_get_data (G_OBJECT (view->mainwin), "toolbar"); 2765 2766 if (strcmp (toolbar_style, "both") == 0) { 2767 gtk_toolbar_set_style (GTK_TOOLBAR (w), GTK_TOOLBAR_BOTH); 2768 }else if (strcmp (toolbar_style, "both-horiz") == 0) { 2769 gtk_toolbar_set_style (GTK_TOOLBAR (w), GTK_TOOLBAR_BOTH_HORIZ); 2770 }else if (strcmp (toolbar_style, "icons") == 0) { 2771 gtk_toolbar_set_style (GTK_TOOLBAR (w), GTK_TOOLBAR_ICONS); 2772 }else if (strcmp (toolbar_style, "text") == 0) { 2773 gtk_toolbar_set_style (GTK_TOOLBAR (w), GTK_TOOLBAR_TEXT); 2774 } 2775 2776 g_free (toolbar_style); 2777 } 2778 2779 /* update name/content conversion mode accordting to command line option */ 2780 if (view->setting->pref->conv_content) { 2781 w = g_object_get_data (G_OBJECT (view->mainwin), "menu_content_mode"); 2782 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE); 2783 } 2784 2785 return; 2786 } 2787