Home | History | Annotate | Download | only in src
      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