00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021  
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 #include "config.h"
00032 
00033 #include <ctype.h>
00034 #include <gtk/gtk.h>
00035 #include <stdlib.h>
00036 #include <sys/stat.h>
00037 #include <sys/time.h>
00038 #include <time.h>
00039 
00040 #include "ctb_log.h"
00041 #include "filemodel.h"
00042 #include "filetypes.h"
00043 #include "i18n.h"
00044 #include "ipc.h"
00045 #include "menu.h"
00046 #include "shortcut.h"
00047 #include "db.h"
00048 #include "ctb_actions.h"
00049 
00050 
00051 
00052 
00053 
00054 
00055 static const char* order_names[N_CTB_SORT_ORDER] =
00056         {
00057                 
00058                 [CTB_SORT_BY_NAME]       = N_("- by title"),
00059                 
00060                 [CTB_SORT_BY_TYPE]       = N_("- by type"),
00061                 
00062                 [CTB_SORT_BY_SIZE]       = N_("- by size"),
00063                 
00064                 [CTB_SORT_BY_DATE_ADDED] = N_("- by date added"),
00065                 
00066                 [CTB_SORT_BY_DATE_READ]  = N_("- recently opened"),
00067                 
00068                 [CTB_SORT_BY_AUTHOR]     = N_("- by author"),
00069         };
00070 
00071 typedef struct
00072         {
00073             gchar   *parent_dir;
00074             gchar   *last_item;
00075         } stack_entry_t;
00076 
00077 typedef struct
00078     {
00079         const char* title;
00080         const char* tag_filter;
00081         ctb_sort_order_t order;
00082         gboolean fixed_order;
00083     } ViewModeInfo;
00084 
00085 #define TAG_BOOK     "book"
00086 #define TAG_IMAGE    "image"
00087 #define TAG_NEWS     "news"
00088 #define TAG_PERSONAL "personal"
00089 #define TAG_HELP     "help"
00090 #define TAG_NOTE     "note"
00091 
00092 static ViewModeInfo g_viewmodes[] =
00093     {   
00094         
00095         [DESKTOP_VIEW]  = { N_("Home"),               NULL,         CTB_SORT_BY_NAME,       TRUE },
00096         [SETTINGS_VIEW] = { N_("Settings"),           NULL,         CTB_SORT_BY_NAME,       TRUE },
00097         [BOOKS_VIEW]    = { N_("Books"),              TAG_BOOK,     CTB_SORT_BY_NAME,       FALSE},
00098         [NEWS_VIEW]     = { N_("News"),               TAG_NEWS,     CTB_SORT_BY_DATE_ADDED, FALSE},
00099         [IMAGES_VIEW]   = { N_("Images"),             TAG_IMAGE,    CTB_SORT_BY_NAME,       FALSE},
00100         [PERSONAL_VIEW] = { N_("Personal Documents"), TAG_PERSONAL, CTB_SORT_BY_NAME,       FALSE},
00101         [DIR_VIEW]      = { N_("SD Card"),            NULL,         CTB_SORT_BY_NAME,       FALSE},
00102         [SHORTCUT_VIEW] = { N_("Shortcuts"),          NULL,         CTB_SORT_BY_NAME,       FALSE},
00103         [NOTES_VIEW]    = { N_("Notes"),              TAG_NOTE,     CTB_SORT_BY_DATE_ADDED, FALSE},
00104         [HELP_VIEW]     = { N_("Help"),               TAG_HELP,     CTB_SORT_BY_NAME,       FALSE},
00105         [RECENT_VIEW]   = { N_("Recently Added"),     NULL,         CTB_SORT_BY_DATE_ADDED, TRUE },
00106         [SEARCH_VIEW]   = { N_("Search Results"),     NULL,         CTB_SORT_BY_NAME,       FALSE},
00107     };
00108 
00109 
00110 
00111 
00112 
00113 static const char *THUMBNAIL_IMAGE_TYPE = "png";
00114 static int NUM_RECENT_ITEMS = 15;
00115 
00116 
00117 
00118 
00119 
00120 
00121 static GtkListStore          *g_filestore    = NULL;            
00122 static gint                  g_items_per_page     = 1;               
00123 static gint                  g_item_offset   = 0;               
00124 static gint                  g_total_items   = 0;               
00125 static gboolean              g_has_next_page = FALSE;           
00126 static int                   g_curpage       = 0;               
00127 static int                   g_numpages      = 0;               
00128 static gboolean              g_boundary_check = TRUE;           
00129 static ctb_viewmodes_t       g_viewmode      = BROWSE_MODE;
00130 static int                   g_desktop_offset = 0;
00131 static int                   g_desktop_items  = 0;
00132 
00133 static ctb_sort_order_t      g_sort_order    = -1;
00134 static gboolean              g_is_sort_asc   = TRUE;            
00135 static filemodel_thumbsize_t g_thumbnail_size= MODTHUMB_MEDIUM; 
00136 
00137 static gchar                 *g_current_dir  = NULL;            
00138 static metadata_table        *g_values       = NULL;            
00139 static GSList                *toggled_list   = NULL;            
00140 static gchar                 *g_delete_text  = NULL;            
00141 static time_t                g_last_modified = 0;               
00142 
00143 static GList                 *g_dir_stack    = NULL;            
00144 
00145 static char g_alpha_list[FILEMODEL_NUM_ALPHA]; 
00146 static GSList                *desktop_names  = NULL;            
00147 
00148 static gchar                 *g_search_filter = NULL;
00149 static const gchar           *g_subtitle     = NULL;
00150 
00151 #if MACHINE_IS_DR800S || MACHINE_IS_DR800SG || MACHINE_IS_DR800SW
00152 static filelist_entry_t      *g_last_read      = NULL;
00153 static metadata_table        *g_last_read_results = NULL;
00154 #endif
00155 
00156 
00157 static gboolean g_window_is_on_top = TRUE;                      
00158 static ViewMode              g_viewmode2 = -1;
00159 
00160 
00161 
00162 
00163 
00164 static gchar*         format_size                       ( const gint64 bytes );
00165 static void           load_items_in_model               ( void );
00166 static void           clear_toggled_list                ( void );
00167 static gboolean       is_delete_allowed(const gchar* filename, const gchar* dirpath);
00168 static void           scroll_to_filename(const gchar *filename);
00169 static void           update_alpha_list();
00170 static void           clear_dir_stack();
00171 static GdkPixbuf*     get_thumbnail(const metadata_cell *cell);
00172 static GdkPixbuf*     create_delete_overlay(const GdkPixbuf* source, gboolean toggled);
00173 static int            num_specials_per_page();
00174 
00175 
00176 
00177 
00178 
00179 
00180 void filemodel_init()
00181 {
00182     filetypes_init(TRUE);
00183     get_filemodel();
00184     filemodel_set_viewmode2(DESKTOP_VIEW);
00185 }
00186 
00187 
00188 erMetadb filemodel_get_database()
00189 {
00190     return get_database();
00191 }
00192 
00193 
00194 static void clear_last_read()
00195 {
00196 #if MACHINE_IS_DR800S || MACHINE_IS_DR800SG || MACHINE_IS_DR800SW
00197     filelist_entry_free(g_last_read);
00198     g_last_read = NULL;
00199     metadata_table_free(g_last_read_results);
00200     g_last_read_results = NULL;
00201 #endif
00202 }
00203 
00204 
00205 void filemodel_quit()
00206 {
00207     close_database();
00208     if (g_values) metadata_table_free(g_values);
00209     g_values = NULL;
00210     clear_last_read();
00211     clear_toggled_list();
00212 }
00213 
00214 
00215 GtkTreeModel* get_filemodel()
00216 {
00217     if (g_filestore == NULL)
00218     {
00219         g_filestore = gtk_list_store_new(N_FILEMODEL_COLUMNS, FILEMODEL_COLUMN_TYPES);
00220     }
00221     return GTK_TREE_MODEL(g_filestore);
00222 }
00223 
00224 
00225 static const char* get_row_filename(const metadata_cell *row)
00226 {
00227     const metadata_cell *cell = row; 
00228     if (cell->type != METADATA_TEXT) {
00229         ERRORPRINTF("illegal cell type [%d] for filename", cell->type);
00230         return NULL;
00231     }
00232     return cell->value.v_text->str;
00233 }
00234 
00235 
00236 static const char* get_row_dirpath(const metadata_cell *row)
00237 {
00238     const metadata_cell *cell = row + 1;        
00239     if (cell->type != METADATA_TEXT) {
00240         ERRORPRINTF("illegal cell type [%d] for directory_path", cell->type);
00241         return NULL;
00242     }
00243     return cell->value.v_text->str;
00244 }
00245 
00246 
00247 static gboolean get_row_is_directory(const metadata_cell *row)
00248 {
00249     const metadata_cell *cell = row + 2;        
00250     if (cell->type != METADATA_INT64) {
00251         ERRORPRINTF("illegal cell type [%d] for is_directory", cell->type);
00252         return FALSE;
00253     }
00254     gboolean is_directory = FALSE;
00255     gint64 i64 = cell->value.v_int64;
00256     if (i64 == 1) {
00257         is_directory = TRUE;
00258     } else if (i64 != 0) {
00259         ERRORPRINTF("illegal value [%lld] for is_directory", i64);
00260     }
00261     return is_directory;
00262 }
00263 
00264 
00265 static const char* get_row_filetype(const metadata_cell *row)
00266 {
00267     const metadata_cell *cell = row + 3;        
00268     if (cell->type != METADATA_TEXT) {
00269         ERRORPRINTF("illegal cell type [%d] for file_type", cell->type);
00270         return NULL;
00271     }
00272     return cell->value.v_text->str;
00273 }
00274 
00275 
00276 static gint64 get_row_filesize(const metadata_cell *row)
00277 {
00278     const metadata_cell *cell = row + 4;        
00279     if (cell->type != METADATA_INT64)
00280     {
00281         if (cell->type != METADATA_NULL) {
00282             ERRORPRINTF("illegal cell type [%d] for filesize", cell->type);
00283         }
00284         return 0;
00285     }
00286     gint64 i64 = cell->value.v_int64;
00287     g_assert(i64 >= 0);
00288     return i64;
00289 }
00290 
00291 
00292 static gint64 get_row_filedate(const metadata_cell *row)
00293 {
00294     const metadata_cell *cell = row + 5;        
00295     if (cell->type != METADATA_INT64)
00296     {
00297         if (cell->type != METADATA_NULL) {
00298             ERRORPRINTF("illegal cell type [%d] for filedate", cell->type);
00299         }
00300         return 0;
00301     }
00302     gint64 i64 = cell->value.v_int64;
00303     g_assert(i64 >= 0);
00304     return i64;
00305 }
00306 
00307 
00308 static const char* get_row_title(const metadata_cell *row)
00309 {
00310     const metadata_cell *cell = row + 6;        
00311     if (cell->type != METADATA_TEXT) {
00312         if (cell->type != METADATA_NULL) {
00313             ERRORPRINTF("illegal cell type [%d] for title", cell->type);
00314         }
00315         return NULL;
00316     }
00317     return cell->value.v_text->str;
00318 }
00319 
00320 
00321 static const char* get_row_author(const metadata_cell *row)
00322 {
00323     const metadata_cell *cell = row + 7;        
00324     if (cell->type != METADATA_TEXT) {
00325         if (cell->type != METADATA_NULL) {
00326             ERRORPRINTF("illegal cell type [%d] for author", cell->type);
00327         }
00328         return NULL;
00329     }
00330     return cell->value.v_text->str;
00331 }
00332 
00333 
00334 static const guchar* get_row_thumbnail(const metadata_cell *row, int* size)
00335 {
00336     const metadata_cell *cell = row + 8;        
00337     if (cell->type != METADATA_BLOB) {
00338         if (cell->type != METADATA_NULL) {
00339             ERRORPRINTF("illegal cell type [%d] for thumbnail", cell->type);
00340         }
00341         *size = 0;
00342         return NULL;
00343     }
00344     *size = cell->value.v_blob.len;
00345     return (guchar*)cell->value.v_blob.data;
00346 }
00347 
00348 
00349 static int get_thumbnail_column()
00350 {
00351     switch (g_thumbnail_size) {
00352         case MODTHUMB_MINI:
00353             return COL_THUMB_MINI;
00354         case MODTHUMB_SMALL:
00355             return COL_THUMB_SMALL;
00356         case MODTHUMB_MEDIUM:
00357             return COL_THUMB_MEDIUM;
00358         case MODTHUMB_LARGE:
00359             return COL_THUMB_LARGE;
00360         default:
00361             ERRORPRINTF("unknown g_thumbnail_size [%d]", g_thumbnail_size);
00362             return -1;
00363     }
00364 }
00365 
00366 
00367 static int create_query()
00368 {
00369     int thumbcolumn = get_thumbnail_column();
00370     if (thumbcolumn == -1) return ER_FAIL;
00371 
00372     
00373     int rc= db_query_create(COL_FILENAME,
00374                         COL_DIRECTORY_PATH,
00375                         COL_IS_DIRECTORY,
00376                         COL_FILETYPE,
00377                         COL_FILESIZE,
00378                         COL_FILETIME_MODIFIED,
00379                         COL_TITLE,
00380                         COL_AUTHOR,
00381                         thumbcolumn,
00382                         -1);
00383     if (rc != ER_OK)  ERRORPRINTF("error creating query");
00384     return rc;
00385 }
00386 
00387 
00388 static time_t get_last_modified()
00389 {
00390     const gchar* mountpoint = ipc_get_media();
00391     if (mountpoint == NULL) return 0;
00392 
00393     char database_file[256];
00394     sprintf(database_file, "%s/%s", mountpoint, ERMETADB_GLOBAL_DATABASE_FILE);
00395 
00396     struct stat statbuf;
00397     stat(database_file, &statbuf);
00398     time_t date_modified = statbuf.st_mtime;
00399     return date_modified;
00400 }
00401 
00402 
00403 static gboolean is_database_changed()
00404 {
00405     return g_last_modified != get_last_modified();
00406 }
00407 
00408 
00409 
00410 static void load_dir_from_metadb()
00411 {
00412     LOGPRINTF("%s() thumbsize=%d", __func__, g_thumbnail_size);
00413 
00414 #if (TIMING_ON)
00415     struct timeval t1, t2;
00416     gettimeofday(&t1, NULL);
00417 #endif
00418 
00419     if (g_current_dir == NULL) goto out;
00420 
00421     
00422     if (g_values) metadata_table_free(g_values);
00423     g_values = NULL;
00424     g_total_items = 0;
00425     g_item_offset = 0;
00426     clear_toggled_list();
00427     bzero(g_alpha_list, sizeof(g_alpha_list));
00428 
00429     if (filemodel_current_is_desktop()) goto out;
00430 
00431     g_last_modified = get_last_modified();
00432 
00433     int rc = create_query();
00434     if (rc != ER_OK) goto out;
00435 
00436     switch (g_viewmode2) {
00437         case DESKTOP_VIEW:
00438             
00439             break;
00440         case SETTINGS_VIEW:
00441             
00442             rc = db_query_execute(g_sort_order,
00443                                   g_is_sort_asc,
00444                                   &g_values,
00445                                   NULL);
00446             break;
00447         case BOOKS_VIEW:
00448         case NEWS_VIEW:
00449         case IMAGES_VIEW:
00450         case PERSONAL_VIEW:
00451         case HELP_VIEW:
00452         case NOTES_VIEW:
00453             
00454             g_assert(g_viewmodes[g_viewmode2].tag_filter);
00455             rc = db_query_execute(g_sort_order,
00456                                   g_is_sort_asc,
00457                                   &g_values,
00458                                   g_viewmodes[g_viewmode2].tag_filter);
00459             break;
00460         case DIR_VIEW:
00461             
00462             rc = db_query_execute_path_filter(g_sort_order,
00463                                               g_is_sort_asc,
00464                                               &g_values,
00465                                               g_current_dir,
00466                                               TRUE);
00467             break;
00468         case SHORTCUT_VIEW:
00469             
00470             rc = db_query_execute_path_filter(g_sort_order,
00471                                               g_is_sort_asc,
00472                                               &g_values,
00473                                               g_current_dir,
00474                                               FALSE);
00475             break;
00476         case RECENT_VIEW:
00477             rc = db_query_execute_recent(&g_values, NUM_RECENT_ITEMS);
00478             break;
00479         case SEARCH_VIEW:
00480             
00481             rc = db_query_execute_search_filter(g_sort_order,
00482                                                 g_is_sort_asc,
00483                                                 &g_values,
00484                                                 g_search_filter);
00485             break;
00486     }
00487 
00488     if (rc != ER_OK)
00489     {
00490         ERRORPRINTF("cannot select metadata");
00491         goto out;
00492     }
00493     
00494     if (g_values)
00495     {
00496         g_total_items = metadata_table_n_rows(g_values);
00497         update_alpha_list();
00498     }
00499 out:
00500 #if (TIMING_ON)
00501     gettimeofday(&t2, NULL);
00502     float cost = (float) (((t2.tv_sec - t1.tv_sec) * 1000 * 1000) + (t2.tv_usec - t1.tv_usec));
00503     printf("%s() items=%d  duration=%4.1lf ms\n", __func__, g_total_items, cost/1000);
00504 #endif
00505     return;
00506 }
00507 
00508 
00509 static void set_sortorder(ctb_sort_order_t sort_order)
00510 {
00511     if (g_sort_order == sort_order) return;
00512 
00513     static const gboolean is_sort_ascending[N_CTB_SORT_ORDER] =
00514                 {
00515                     [CTB_SORT_BY_NAME]       = TRUE,
00516                     [CTB_SORT_BY_TYPE]       = TRUE,
00517                     [CTB_SORT_BY_SIZE]       = FALSE,
00518                     [CTB_SORT_BY_DATE_ADDED] = FALSE,
00519                     [CTB_SORT_BY_DATE_READ]  = FALSE,
00520                     [CTB_SORT_BY_AUTHOR]     = TRUE,
00521                 };
00522 
00523     g_sort_order = sort_order;
00524     g_is_sort_asc = is_sort_ascending[sort_order];
00525     menu_select_sort_order(sort_order);
00526     g_subtitle = gettext (order_names[sort_order]);
00527 }
00528 
00529 
00530 gboolean filemodel_set_sortorder (ctb_sort_order_t sort_order,
00531                                   const gchar* cursor_item,
00532                                   gboolean updateScreen)
00533 {
00534     if (g_sort_order == sort_order) return FALSE;
00535 
00536     g_assert(!g_viewmodes[g_viewmode2].fixed_order);
00537     g_viewmodes[g_viewmode2].order = sort_order;
00538     set_sortorder(sort_order);
00539 
00540     if (updateScreen) {
00541         load_dir_from_metadb();
00542         if (g_values) scroll_to_filename(cursor_item);
00543         load_items_in_model();
00544     }
00545     return TRUE;
00546 }
00547 
00548 
00549 gboolean filemodel_show_alphabar() {
00550     
00551     if (g_viewmode2 == DESKTOP_VIEW || g_viewmode2 == SETTINGS_VIEW) return FALSE;
00552 
00553     
00554     if (g_sort_order == CTB_SORT_BY_NAME || g_sort_order == CTB_SORT_BY_AUTHOR) {
00555         return TRUE;
00556     }
00557     return FALSE;
00558 }
00559 
00560 
00561 gboolean filemodel_has_searchbutton()
00562 {
00563     if (g_viewmode2 == DESKTOP_VIEW || g_viewmode2 == SETTINGS_VIEW) return FALSE;
00564     return TRUE;
00565 }
00566 
00567 
00568 void filemodel_set_viewmode2(ViewMode newmode)
00569 {
00570     LOGPRINTF("viewmode %d -> %d", g_viewmode2, newmode);
00571     if (g_viewmode2 != newmode) {
00572         g_viewmode2 = newmode;
00573         set_sortorder(g_viewmodes[newmode].order);
00574         const char* category = NULL;
00575         if (newmode != DESKTOP_VIEW) {
00576             category = g_viewmodes[newmode].title;
00577         }
00578         menu_set_category(category);
00579     }
00580 }
00581 
00582 
00583 void filemodel_set_search_filter(const gchar* search_string)
00584 {
00585     g_free(g_search_filter);
00586     g_search_filter = NULL;
00587     if (search_string == NULL || strcmp(search_string, "") == 0) return;
00588     g_search_filter = g_strdup(search_string);
00589 }
00590 
00591 
00592 const gchar* filemodel_get_title()
00593 {
00594     return _(g_viewmodes[g_viewmode2].title);
00595 }
00596 
00597 
00598 const gchar* filemodel_get_subtitle()
00599 {
00600     if (g_viewmodes[g_viewmode2].fixed_order) return "";
00601     else return g_subtitle;
00602 }
00603 
00604 
00605 static gboolean is_dir_on_media(const char* dir)
00606 {
00607     const char* media = ipc_get_media();
00608     if (!media) return FALSE;
00609 
00610     int len = strlen(media);
00611     if (strncmp(dir, media, len) == 0 &&
00612        (dir[len] == '/' || dir[len] == '\0')) return TRUE;
00613     return FALSE;
00614 }
00615 
00616 
00617 static gboolean filemodel_current_is_media()
00618 {
00619     return is_dir_on_media(g_current_dir);
00620 }
00621 
00622 
00623 const gchar* filemodel_get_menu_content()
00624 {
00625     if (g_viewmode == DELETE_MODE) return MENU_CONTENT_DELETE_MODE;
00626 
00627     if (g_viewmode2 == RECENT_VIEW) return MENU_CONTENT_RECENT_MODE;
00628 
00629     if (filemodel_current_is_media()) return MENU_CONTENT_MEDIA;
00630 
00631     return MENU_CONTENT;
00632 }
00633 
00634 
00635 gboolean filemodel_set_viewsize (const int n_items, gboolean updateScreen)
00636 {
00637     LOGPRINTF("items=%d->%d  updateScreen=%d", g_items_per_page, n_items, updateScreen);
00638     g_return_val_if_fail(n_items > 0  && n_items < 100, FALSE);
00639     if (n_items == g_items_per_page) return FALSE;
00640 
00641     g_items_per_page = n_items;
00642 
00643     
00644     int desktop_page = g_desktop_offset / g_items_per_page;
00645     g_desktop_offset = desktop_page * g_items_per_page;
00646 
00647     
00648     int page = g_item_offset / g_items_per_page;
00649     g_item_offset = page * g_items_per_page;
00650 
00651     if (updateScreen) {
00652         load_items_in_model();
00653     }
00654     return TRUE;
00655 }
00656 
00657 
00658 void filemodel_set_viewmode(ctb_viewmodes_t newmode, gboolean updateScreen)
00659 {
00660     LOGPRINTF("mode: %d->%d  update=%d", g_viewmode, newmode, updateScreen);
00661     g_viewmode = newmode;
00662     clear_toggled_list();
00663     if (updateScreen) {
00664         load_items_in_model();
00665     }
00666     menu_update_view_mode(g_viewmode);
00667     ipc_menu_set_pagecounter(g_curpage, g_numpages, g_boundary_check);
00668 }
00669 
00670 
00671 void filemodel_set_thumbsize(const filemodel_thumbsize_t thumb_size, gboolean reload)
00672 {
00673     LOGPRINTF("thumb_size [%d]", thumb_size);
00674 
00675     if (thumb_size == g_thumbnail_size) return;
00676 
00677     g_thumbnail_size = thumb_size;
00678     if (reload) load_dir_from_metadb();
00679     
00680     gtk_list_store_clear(g_filestore);
00681 }
00682 
00683 
00684 
00685 
00686 static int num_items_per_page()
00687 {
00688     int num_per_page = g_items_per_page;
00689     num_per_page -= num_specials_per_page();
00690     if (num_per_page <= 0) num_per_page = 1;
00691     return num_per_page;
00692 }
00693 
00694 
00695 gboolean filemodel_has_prev_page()
00696 {
00697     if (filemodel_current_is_desktop()) return (g_desktop_offset > 0);
00698     else return (g_item_offset > 0);
00699 }
00700 
00701 
00702 gboolean filemodel_has_next_page()
00703 {
00704     return g_has_next_page;
00705 }
00706 
00707 
00708 void filemodel_page_previous()
00709 {
00710     if (filemodel_current_is_desktop()) {
00711         if (g_desktop_offset > 0) {
00712             g_desktop_offset -= g_items_per_page;
00713             if (g_desktop_offset < 0) g_desktop_offset = 0;
00714             load_items_in_model();
00715         }
00716     } else {
00717         if (g_item_offset > 0) {
00718             g_item_offset -= num_items_per_page();
00719             if (g_item_offset < 0) g_item_offset = 0;
00720             load_items_in_model();
00721         }
00722     }
00723 }
00724 
00725 
00726 void filemodel_page_next()
00727 {
00728     if (g_has_next_page)
00729     {
00730         LOGPRINTF("entry: old offset [%d] page size [%d]", g_item_offset, g_items_per_page);
00731         if (filemodel_current_is_desktop()) {
00732             g_desktop_offset += g_items_per_page;
00733             g_assert(g_desktop_offset < g_desktop_items);
00734         } else {
00735             g_item_offset += num_items_per_page();
00736             g_assert(g_item_offset < g_total_items);
00737         }
00738         load_items_in_model();
00739     }
00740 }
00741 
00742 
00743 void filemodel_update_pagecounter()
00744 {
00745     ipc_menu_set_pagecounter(g_curpage, g_numpages, g_boundary_check);
00746 }
00747 
00748 
00749 static void scroll_to_filename(const gchar *filename)
00750 {
00751     LOGPRINTF("filename=%s", filename);
00752     gint item_idx  = 0;
00753 
00754     if (g_values && filename && *filename)
00755     {
00756         int row;
00757         int columns = g_values->n_columns;
00758         const metadata_cell *cell = (const metadata_cell*) (g_values->cell_data->data);
00759         for (row = 0; row < g_total_items; row++) {
00760             const gchar* name = get_row_filename(cell);
00761             if (strcmp( filename, name) == 0) {
00762                 item_idx = row;
00763                 break;
00764             }
00765             cell += columns;
00766         }
00767     }
00768 
00769     int num_per_page = num_items_per_page();
00770     int page = item_idx / num_per_page;
00771     g_item_offset = page * num_per_page;
00772 }
00773 
00774 
00775 static const gchar* get_sorted_name(const metadata_cell* row)
00776 {
00777     const gchar* name = NULL;
00778     if (g_sort_order == CTB_SORT_BY_NAME) {
00779         if (g_viewmode2 == DIR_VIEW) {
00780             name = get_row_filename(row);
00781         } else {
00782             name = get_row_title(row);
00783             if (name == NULL) name = get_row_filename(row);
00784         }
00785     } else if (g_sort_order == CTB_SORT_BY_AUTHOR) {
00786         name = get_row_author(row);
00787     }
00788     return name;
00789 }
00790 
00791 
00792 static unsigned int alpha2index(char alpha)
00793 {
00794     if (isalpha(alpha)) {
00795         int letter = toupper(alpha);
00796         return (1 + letter - 'A');  
00797     } else {
00798         return 0;   
00799     }
00800 }
00801 
00802 
00803 int filemodel_scroll_to_letter(gchar letter, gboolean jump_to_dir, gboolean* same_page)
00804 {
00805     g_assert(g_values);
00806     gint item_idx  = 0;
00807     gboolean found = FALSE;
00808 
00809     gboolean first_ok = TRUE;
00810     int index = alpha2index(letter);
00811     if (g_alpha_list[index] == (ALPHA_HAS_DIR|ALPHA_HAS_FILE)) first_ok = FALSE;
00812 
00813     int row;
00814     const metadata_cell *cell = (const metadata_cell*) (g_values->cell_data->data);
00815     for (row = 0; row < g_total_items; row++) {
00816         const gchar* name = get_sorted_name(cell);
00817         if (name) {
00818             char current = name[0];
00819             if (current == letter || current == tolower(letter) ||  
00820                 (letter == '#' && !isalpha(current))) {             
00821                 
00822                 if (first_ok || jump_to_dir == get_row_is_directory(cell)) {
00823                     item_idx = row;
00824                     found = TRUE;
00825                     break;
00826                 }
00827             }
00828         }
00829         cell += g_values->n_columns;
00830     }
00831 
00832     int num_per_page = num_items_per_page();
00833     if (item_idx >= g_item_offset && item_idx < (g_item_offset + num_per_page)) {
00834         *same_page = TRUE;
00835     } else {
00836         *same_page = FALSE;
00837     }
00838 
00839     int page = item_idx / num_per_page;
00840     g_item_offset = page * num_per_page;
00841     
00842     if (found) {
00843         item_idx = item_idx % num_per_page;
00844         item_idx += num_specials_per_page();
00845     } else {
00846         
00847         item_idx = -1;
00848     }
00849     if (!*same_page) load_items_in_model();
00850     return item_idx;
00851 }
00852 
00853 
00854 int filemodel_get_first_alpha_index()
00855 {
00856     if (g_total_items == 0) return -1;
00857     g_assert(g_values);
00858 
00859     const metadata_cell *cell = (const metadata_cell*) (g_values->cell_data->data);
00860     cell += (g_item_offset * g_values->n_columns);
00861     const gchar* name = get_sorted_name(cell);
00862     if (name == NULL) return -1;
00863     return alpha2index(name[0]);
00864 }
00865 
00866 
00867 void filemodel_scroll_to_filename (const gchar *filename)
00868 {
00869     scroll_to_filename(filename);
00870     load_items_in_model();
00871 }
00872 
00873 
00874 int filemodel_get_display_index(const gchar *filename)
00875 {
00876     if (!filename || !*filename) return 0;
00877 
00878     
00879     if (filemodel_current_is_desktop()) {
00880         int index = 0;
00881         GSList *iter = desktop_names;
00882         while (iter) {
00883             if (strcmp(filename, (const char*)iter->data) == 0) return index;
00884             index++;
00885             iter = g_slist_next(iter);
00886         }
00887     }
00888     else if (g_values)
00889     {
00890         int num_per_page = num_items_per_page();
00891         int columns = g_values->n_columns;
00892         const metadata_cell *cell = (const metadata_cell*) (g_values->cell_data->data);
00893         
00894         cell += (g_item_offset * g_values->n_columns);
00895         
00896         int row;
00897         for (row=g_item_offset; row < (g_item_offset + num_per_page) && row < g_total_items ; row++)
00898         {
00899             g_assert(cell->type == METADATA_TEXT);
00900             if (strcmp( filename, cell->value.v_text->str) == 0) {
00901                 int item_idx = row - g_item_offset;
00902                 item_idx += num_specials_per_page();
00903                 return item_idx;
00904                 break;
00905             }
00906             cell += columns;
00907         }
00908     }
00909 
00910     return 0;
00911 }
00912 
00913 
00914 
00915 
00916 static int filemodel_chdir(const gchar *dir, const gchar *cursor_item)
00917 {
00918     LOGPRINTF("dir=%s  cursor=%s", dir, cursor_item ? cursor_item : "NULL");
00919     g_assert(dir && *dir);
00920 
00921     int ret = ER_OK;
00922     if (filemodel_current_is_desktop()) {
00923         if (ipc_get_media() == NULL) close_database();
00924         
00925     } else {
00926         if (g_viewmode2 == SETTINGS_VIEW) {
00927             ret = open_global_database(dir);    
00928         } else {
00929             const char* card = ipc_get_media();
00930             ret = open_global_database(card);
00931         }
00932     }
00933     if (ret != ER_OK)
00934     {
00935         ERRORPRINTF("cannot synchronise database, keeping model unchanged");
00936         return ER_FAIL;
00937     }
00938 
00939     g_free(g_current_dir);
00940     g_current_dir = g_strdup(dir);
00941     menu_set_current_is_media(filemodel_current_is_media());
00942     load_dir_from_metadb();
00943     if (g_values) scroll_to_filename(cursor_item);
00944     load_items_in_model();
00945 
00946     return ER_OK;
00947 }
00948 
00949 
00950 int filemodel_chdir_desktop()
00951 {
00952     clear_dir_stack();
00953     g_desktop_offset = 0;
00954     filemodel_set_viewmode2(DESKTOP_VIEW);
00955     return filemodel_chdir(DIR_DESKTOP_INTERNAL, NULL);
00956 }
00957 
00958 
00959 static void free_stack_entry(stack_entry_t *entry)
00960 {
00961     g_assert(entry);
00962     g_free(entry->parent_dir);
00963     g_free(entry->last_item);
00964     g_free(entry);
00965 }
00966 
00967 
00968 static stack_entry_t* create_stack_entry(const gchar* parent, const gchar* item)
00969 {
00970     stack_entry_t *entry = g_new0(stack_entry_t, 1);
00971     entry->parent_dir = g_strdup(parent);
00972     entry->last_item = g_strdup(item);
00973     return entry;
00974 }
00975 
00976 
00977 static void clear_dir_stack()
00978 {
00979     GList *iter = g_dir_stack;
00980     while (iter) {
00981         free_stack_entry(iter->data);
00982         iter = g_list_next(iter);
00983     }
00984     g_list_free(g_dir_stack);
00985     g_dir_stack = NULL;
00986 }
00987 
00988 
00989 int filemodel_chdir_down(const gchar *dir, const gchar *last_item)
00990 {
00991     LOGPRINTF("dir=%s  last_item=%s", dir, last_item);
00992     g_dir_stack = g_list_append(g_dir_stack, create_stack_entry(g_current_dir, last_item));
00993 
00994     filemodel_chdir(dir, NULL);
00995     return 0;
00996 }
00997 
00998 
00999 gchar *filemodel_chdir_up()
01000 {
01001     LOGPRINTF("");
01002     g_assert(g_dir_stack);
01003     GList* last = g_list_last(g_dir_stack);
01004     stack_entry_t *entry = (stack_entry_t*)last->data;
01005     g_dir_stack = g_list_delete_link(g_dir_stack, last);
01006 
01007     if (g_dir_stack)
01008     {
01009         
01010         filemodel_chdir(entry->parent_dir, entry->last_item);
01011     }
01012     else
01013     {
01014         
01015         filemodel_set_viewmode2(DESKTOP_VIEW);
01016         filemodel_chdir(DIR_DESKTOP_INTERNAL, NULL);
01017     }
01018     gchar* last_item = g_strdup(entry->last_item);
01019     free_stack_entry(entry);
01020 
01021     return last_item;
01022 }
01023 
01024 
01025 gboolean filemodel_resync(gboolean force_reload)
01026 {
01027     if (!force_reload && !is_database_changed()) return FALSE;
01028 
01029     if (filemodel_current_is_desktop()) {
01030         if (g_desktop_items <= g_items_per_page) g_desktop_offset = 0;
01031     }
01032     int old_offset = g_item_offset;
01033     load_dir_from_metadb();
01034 
01035     
01036     g_item_offset = old_offset;
01037     int num_per_page = num_items_per_page();
01038     while (g_item_offset >= g_total_items) {
01039         g_item_offset -= num_per_page;
01040     }
01041     if (g_item_offset < 0) g_item_offset = 0;
01042     return TRUE;
01043 }
01044 
01045 
01046 static GdkPixbuf* get_thumbnail_from_data(const guchar* data, int size, const gchar* filename)
01047 {
01048     
01049     GdkPixbufLoader *thumb_ldr = gdk_pixbuf_loader_new_with_type(THUMBNAIL_IMAGE_TYPE, NULL);
01050     g_assert(thumb_ldr);
01051     GdkPixbuf* thumbnail = NULL;
01052 
01053     GError *err = NULL;
01054     gboolean ok = gdk_pixbuf_loader_write(thumb_ldr, data, size, &err);
01055     if (!ok)
01056     {
01057         ERRORPRINTF("cannot load thumbnail for file [%s] - [%s]", filename, err->message);
01058         goto unref;
01059     }
01060 
01061     ok = gdk_pixbuf_loader_close(thumb_ldr, &err);
01062     if (!ok)
01063     {
01064         ERRORPRINTF("cannot close thumbnail for file [%s] - [%s]", filename, err->message);
01065         goto unref;
01066     }
01067 
01068     
01069     thumbnail = gdk_pixbuf_loader_get_pixbuf(thumb_ldr);
01070     if (thumbnail) {
01071         
01072         g_object_ref(thumbnail);
01073     } else {
01074         ERRORPRINTF("cannot get thumbnail pixbuf for file [%s]", filename);
01075     }
01076 unref:
01077     g_clear_error(&err);
01078     g_object_unref(thumb_ldr);
01079     return thumbnail;
01080 }
01081 
01082 
01083 gboolean filemodel_current_is_desktop()
01084 {
01085     return (g_dir_stack == NULL);
01086 }
01087 
01088 
01089 gboolean filemodel_window_is_on_top()
01090 {
01091     return g_window_is_on_top;
01092 }
01093 
01094 
01095 void filemodel_set_window_is_on_top(gboolean is_on_top )
01096 {
01097     g_window_is_on_top = is_on_top;
01098 }
01099 
01100 
01101 const gchar* filemodel_get_current_dir()
01102 {
01103     return g_current_dir;
01104 }
01105 
01106 
01107 int filemodel_get_dir_depth()
01108 {
01109     int depth = 0;
01110     if (g_dir_stack != NULL)
01111     {
01112         depth = g_list_length(g_dir_stack);
01113     }
01114     return depth;
01115 }
01116 
01117 
01118 static void update_alpha_list()
01119 {
01120     bzero(g_alpha_list, sizeof(g_alpha_list));
01121 
01122     g_assert(g_values);
01123 
01124     if (g_sort_order != CTB_SORT_BY_NAME && g_sort_order != CTB_SORT_BY_AUTHOR) return;
01125 
01126     const metadata_cell *cell = (const metadata_cell*) (g_values->cell_data->data);
01127 
01128     int row;
01129     for (row=0; row<g_total_items; row++) {
01130         const gchar* name = get_sorted_name(cell);
01131         if (name != NULL) {
01132             int index = alpha2index(name[0]);
01133             gboolean is_dir = get_row_is_directory(cell);
01134             g_alpha_list[index] |= (is_dir ? ALPHA_HAS_DIR : ALPHA_HAS_FILE);
01135         }
01136 
01137         cell += g_values->n_columns;
01138     }
01139 }
01140 
01141 
01142 const gchar* filemodel_get_alpha_list()
01143 {
01144     return g_alpha_list;
01145 }
01146 
01147 
01148 static int is_row_toggled(int row, const gchar* filename, const gchar* dirpath)
01149 {
01150     if (g_viewmode == DELETE_MODE) {
01151         
01152         if (!is_delete_allowed(filename, dirpath)) return -1;
01153 
01154         
01155         GSList *iter = toggled_list;
01156         while (iter) {
01157             if (row == (int)iter->data) return 1;
01158             iter = g_slist_next(iter);
01159         }
01160     }
01161     return 0;
01162 }
01163 
01164 
01165 static void clear_toggled_list()
01166 {
01167     g_slist_free(toggled_list);
01168     toggled_list = NULL;
01169 }
01170 
01171 
01172 static gint compare_toggled_entries(gconstpointer left, gconstpointer right)
01173 {
01174     return ((int)left > (int)right);
01175 }
01176 
01177 
01178 void filemodel_toggle_entry(int index, int state, GtkTreeIter *iter)
01179 {
01180     index -= num_specials_per_page();
01181     int item_index = g_item_offset + index;
01182     
01183     if (state == 0) {
01184         toggled_list = g_slist_remove(toggled_list, (int*)item_index);
01185     } else {
01186         toggled_list = g_slist_prepend(toggled_list, (int*)item_index);
01187     }
01188 
01189     GtkTreeModel *model = GTK_TREE_MODEL(g_filestore);
01190 
01191     
01192     if (g_viewmode == DELETE_MODE && g_thumbnail_size == MODTHUMB_MEDIUM)
01193     {
01194         const metadata_cell *cell = (const metadata_cell*) (g_values->cell_data->data);
01195         cell += (item_index * g_values->n_columns);
01196         GdkPixbuf *thumbnail = get_thumbnail(cell);
01197         GdkPixbuf *thumbnail_delete = create_delete_overlay(thumbnail, state);
01198 
01199         gtk_list_store_set (GTK_LIST_STORE(model), iter, MODCOL_THUMBNAIL, thumbnail_delete, -1);
01200         g_object_unref(thumbnail);
01201         g_object_unref(thumbnail_delete);
01202     }
01203     gtk_list_store_set (GTK_LIST_STORE(model), iter, MODCOL_TOGGLED, state, -1);
01204 }
01205 
01206 
01207 static void delete_entry_at_offset(int offset)
01208 {
01209     g_assert(offset < g_total_items);
01210     const metadata_cell *cell = (const metadata_cell*) (g_values->cell_data->data);
01211     
01212     cell += (offset * g_values->n_columns);
01213 
01214     const char* filename = get_row_filename(cell);
01215     const char *dirpath = get_row_dirpath(cell);
01216     gboolean is_directory = get_row_is_directory(cell);
01217     const char *filetype = get_row_filetype(cell);
01218 
01219     
01220 #if MACHINE_IS_DR800S || MACHINE_IS_DR800SG || MACHINE_IS_DR800SW
01221     if (g_last_read &&
01222         strcmp(filename, g_last_read->filename->str) == 0 &&
01223         strcmp(dirpath, g_last_read->directory_path->str) == 0)
01224     {
01225         clear_last_read();
01226     }
01227 #endif
01228 
01229     filelist_entry_t *entry = filelist_entry_new();
01230     entry->filename         = g_string_new(filename);
01231     entry->filename_display = NULL;
01232     entry->filetype         = g_string_new(filetype);
01233     entry->directory_path   = g_string_new(dirpath);
01234     entry->is_directory     = is_directory;
01235     delete_item(entry);
01236     filelist_entry_free(entry);
01237 }
01238 
01239 
01240 void filemodel_delete_toggled()
01241 {
01242     if (toggled_list) { 
01243         toggled_list = g_slist_sort(toggled_list, compare_toggled_entries);
01244         GSList *iter = toggled_list;
01245         while (iter) {
01246             int item_offset = (int)iter->data;
01247             delete_entry_at_offset(item_offset);
01248             iter = g_slist_next(iter);
01249         }
01250         clear_toggled_list();
01251         filemodel_resync(FALSE);
01252     }
01253 }
01254 
01255 
01256 int filemodel_num_toggled()
01257 {
01258     int count = 0;
01259     GSList *iter = toggled_list;
01260     while (iter) {
01261         count++;
01262         iter = g_slist_next(iter);
01263     }
01264     return count;
01265 }
01266 
01267 
01268 void filemodel_set_delete_text(const gchar* text)
01269 {
01270     if (g_delete_text != NULL && strcmp(g_delete_text, text) == 0) return;
01271     g_free(g_delete_text);
01272     g_delete_text = g_strdup(text);
01273     if (g_viewmode == DELETE_MODE) {
01274         GtkTreeIter first;
01275         GtkTreeModel *model = GTK_TREE_MODEL(g_filestore);
01276         gboolean valid = gtk_tree_model_get_iter_first(model, &first);
01277         if (valid) {
01278             gtk_list_store_set (GTK_LIST_STORE(model), &first,
01279                                 MODCOL_TITLE, g_delete_text, -1);
01280         }
01281     }
01282 }
01283 
01284 
01285 static gboolean is_delete_allowed(const gchar* filename, const gchar* dirpath) {
01286     
01287     if (strcmp(dirpath, DIR_LIBRARY) == 0 && strcmp(filename, "System") == 0) return FALSE;
01288 
01289     const gchar *dir = dirpath;
01290     if (strcmp(dirpath, ".") == 0) dir = g_current_dir;
01291     char real_dir[PATH_MAX];
01292     char *cp = realpath(dir, real_dir);
01293     if (cp == real_dir) {
01294         
01295         if (strcmp(real_dir, DIR_DESKTOP_INTERNAL) == 0) return FALSE;
01296 
01297         
01298         if (!is_dir_on_media(real_dir)) return FALSE;
01299     }
01300 
01301     return TRUE;
01302 }
01303 
01304 
01305 static void add_previous_dir(const gchar* title, const gchar* subtitle, const gchar* iconname)
01306 {
01307     gtk_list_store_insert_with_values(
01308                     g_filestore,
01309                     NULL,
01310                     g_items_per_page,
01311                     MODCOL_FILENAME,         "..",
01312                     MODCOL_TITLE,            title,
01313                     MODCOL_FILETYPE,         SPECIAL_ITEM_NAME,
01314                     MODCOL_FILETYPE_DISPLAY, "",
01315                     MODCOL_DIRECTORY_PATH,   ".",
01316                     MODCOL_IS_DIRECTORY,     TRUE,
01317                     MODCOL_FILESIZE,         "",
01318                     MODCOL_FILEDATE,         "",
01319                     MODCOL_SUBTITLE,         subtitle,
01320                     MODCOL_THUMBNAIL,        get_icon_from_file(iconname, g_thumbnail_size),
01321                     MODCOL_TOGGLED,          -1,
01322                     -1 );
01323 }
01324 
01325 
01326 static int num_browse_mode_specials()
01327 {
01328     switch (g_viewmode2) {
01329         case DESKTOP_VIEW:
01330             return 0;
01331         case SETTINGS_VIEW:
01332         case BOOKS_VIEW:
01333         case NEWS_VIEW:
01334         case IMAGES_VIEW:
01335         case PERSONAL_VIEW:
01336         case HELP_VIEW:
01337         case SEARCH_VIEW:
01338         case RECENT_VIEW:
01339             return 1;       
01340         case NOTES_VIEW:
01341             return 2;       
01342         case DIR_VIEW:
01343         case SHORTCUT_VIEW:
01344             return 1;       
01345     }
01346     return 0;       
01347 }
01348 
01349 
01350 static void add_browse_mode_specials()
01351 {
01352     switch (g_viewmode2) {
01353         case DESKTOP_VIEW:
01354             
01355             break;
01356         case SETTINGS_VIEW:
01357         case BOOKS_VIEW:
01358         case NEWS_VIEW:
01359         case IMAGES_VIEW:
01360         case PERSONAL_VIEW:
01361         case HELP_VIEW:
01362         case SEARCH_VIEW:
01363         case RECENT_VIEW:
01364             add_previous_dir(_("to Home"), _("Return to the Home screen"), "back");
01365             break;
01366         case NOTES_VIEW:
01367             add_previous_dir(_("to Home"), _("Return to the Home screen"), "back");
01368             gtk_list_store_insert_with_values(
01369                             g_filestore,
01370                             NULL,
01371                             g_items_per_page,
01372                             
01373                             MODCOL_FILENAME,         "new",
01374                             MODCOL_TITLE,            _("New Note"),
01375                             MODCOL_FILETYPE,         "note",
01376                             MODCOL_FILETYPE_DISPLAY, "",
01377                             MODCOL_DIRECTORY_PATH,   "",
01378                             MODCOL_IS_DIRECTORY,     FALSE,
01379                             MODCOL_FILESIZE,         "",
01380                             MODCOL_FILEDATE,         "",
01381                             MODCOL_SUBTITLE,         _("Open a new notepad to write on"),
01382                             MODCOL_THUMBNAIL,        get_icon_from_file("new-note", g_thumbnail_size),
01383                             MODCOL_TOGGLED,          -1,
01384                             -1 );
01385             break;
01386         case DIR_VIEW:
01387         case SHORTCUT_VIEW:
01388             add_previous_dir(_("Up a Level"), "", "parent-dir");
01389             break;
01390     }
01391 }
01392 
01393 
01394 static void add_delete_mode_specials()
01395 {
01396     gtk_list_store_insert_with_values(
01397                     g_filestore,
01398                     NULL,
01399                     g_items_per_page,
01400                     MODCOL_FILENAME,         "DELETE-ACTION",
01401                     MODCOL_TITLE,            g_delete_text,
01402                     MODCOL_FILETYPE,         "",
01403                     MODCOL_FILETYPE_DISPLAY, "",
01404                     MODCOL_DIRECTORY_PATH,   "",
01405                     MODCOL_IS_DIRECTORY,     FALSE,
01406                     MODCOL_FILESIZE,         "",
01407                     MODCOL_FILEDATE,         "",
01408                     MODCOL_SUBTITLE,           "",
01409                     MODCOL_THUMBNAIL,        get_icon_from_file("delete-action", g_thumbnail_size),
01410                     MODCOL_TOGGLED,          -1,
01411                     -1 );
01412 }
01413 
01414 
01415 
01416 static int num_specials_per_page()
01417 {
01418     switch (g_viewmode) {
01419     case BROWSE_MODE:
01420         return num_browse_mode_specials();
01421     case DELETE_MODE:
01422         return 1;
01423     }
01424     return 0;
01425 }
01426 
01427 
01428 static void add_special_items()
01429 {
01430     switch (g_viewmode) {
01431     case BROWSE_MODE:
01432         add_browse_mode_specials();
01433         break;
01434     case DELETE_MODE:
01435         add_delete_mode_specials();
01436         break;
01437     }
01438 }
01439 
01440 
01441 
01442 
01443 static void update_numpages(int items_per_page)
01444 {
01445     LOGPRINTF("items_per_page=%d", items_per_page);
01446     int first = 0;
01447     int total = 0;
01448     if (filemodel_current_is_desktop()) {
01449         first = MIN(g_desktop_offset + 1, g_desktop_items);
01450         total = g_desktop_items;
01451         g_has_next_page = (g_desktop_offset + items_per_page < total);
01452     } else {
01453         
01454         
01455         if (g_item_offset % items_per_page != 0) {
01456             int offset = g_item_offset % items_per_page;
01457             g_item_offset -= offset;
01458             if (g_item_offset < 0) g_item_offset = 0;
01459         }
01460 
01461         first = MIN(g_item_offset + 1, g_total_items);
01462         total = g_total_items;
01463         g_has_next_page = (g_item_offset + items_per_page < total);
01464     }
01465 
01466     g_curpage = (first / items_per_page) + 1;   
01467     g_numpages = total / items_per_page;
01468     if (total % items_per_page != 0) g_numpages++;
01469     if (g_numpages == 0) g_numpages = 1;
01470 
01471     if (filemodel_current_is_desktop() && g_numpages == 1) {
01472         g_curpage = 0;
01473         g_numpages = 0;
01474     }
01475 
01476     if (filemodel_window_is_on_top())
01477     {
01478         ipc_menu_set_pagecounter(g_curpage, g_numpages, g_boundary_check);
01479     }
01480 }
01481 
01482 
01483 static GdkPixbuf* get_thumbnail_for_shortcut(shortcut_t *shortcut,
01484                                       gboolean is_directory,
01485                                       const gchar* filetype,
01486                                       const gchar* filename,
01487                                       const gchar* dir)
01488 {
01489     GdkPixbuf* thumbnail = NULL;
01490     
01491     switch (shortcut->type)
01492     {
01493         case SHORTCUT_TO_FILE:
01494         {
01495             
01496             const gchar *ext = g_extension_pointer(shortcut->details.file.filename);
01497             thumbnail = get_icon_from_file_extension( ext,
01498                                                       is_directory,
01499                                                       g_thumbnail_size );
01500             break;
01501         }
01502         case SHORTCUT_TO_FOLDER:
01503         {
01504             
01505             char *cp = shortcut->details.folder.directory;
01506             if (   cp
01507                 && shortcut->details.folder.filename == NULL )
01508             {
01509                 if ( strcmp(cp, DIR_LIBRARY) == 0 )
01510                 {
01511                     
01512                     thumbnail = get_icon_library( g_thumbnail_size );
01513                 }
01514                 else
01515                 {
01516                     int n = strlen(DIR_STORAGE_MNT);
01517                     if (   strncmp(cp, DIR_STORAGE_MNT, n) == 0
01518                         && cp[n] == '/' )
01519                     {
01520                         
01521                         cp = strchr( cp + n + 1, '/' );
01522                         if ( cp == NULL )
01523                         {
01524                             
01525                             thumbnail = get_icon_library( g_thumbnail_size );
01526                         }
01527                     }
01528                 }
01529             }
01530 
01531             
01532             if (thumbnail == NULL)
01533             {
01534                 thumbnail = get_icon_from_file_extension( "",
01535                                                           TRUE,        
01536                                                           g_thumbnail_size );
01537             }
01538             break;
01539         }
01540         case SHORTCUT_TO_APPLICATION:
01541             
01542             thumbnail = get_icon_application( g_thumbnail_size );
01543             break;
01544         
01545         case SHORTCUT_TO_WEB_LOCATION:
01546             thumbnail = get_icon_from_file_extension( "html",
01547                                                       FALSE,
01548                                                       g_thumbnail_size );
01549         
01550             break;
01551 
01552         default:
01553             WARNPRINTF("unknown shortcut type [%d] file [%s]", shortcut->type, filename);
01554 
01555             
01556             thumbnail = get_icon_from_file_extension( filetype,
01557                                                       is_directory,
01558                                                       g_thumbnail_size );
01559     }
01560 
01561     
01562     if (thumbnail)
01563     {
01564         if ( strcmp(dir, DIR_DESKTOP_INTERNAL) != 0 )
01565         {
01566             thumbnail = gdk_pixbuf_copy(thumbnail);
01567             apply_icon_overlay_shortcut(g_thumbnail_size, thumbnail);
01568         }
01569         else
01570         {
01571             g_object_ref(thumbnail);
01572         }
01573     }
01574     return thumbnail;
01575 }
01576 
01577 
01578 static gboolean desktop_item_visible(int index)
01579 {
01580     if (index >= g_desktop_offset &&
01581         index < g_desktop_offset + g_items_per_page) return TRUE;
01582     return FALSE;
01583 }
01584 
01585 
01586 static void add_desktop_item(int index,
01587                              const gchar* filename,
01588                              const gchar* title,
01589                              const gchar* subtitle,
01590                              const gchar* path,
01591                              const gchar* iconname)
01592 {
01593     if (!desktop_item_visible(index)) return;
01594     gtk_list_store_insert_with_values(
01595                     g_filestore,
01596                     NULL,
01597                     g_items_per_page,
01598                     MODCOL_FILENAME,         filename,
01599                     MODCOL_TITLE,            title,
01600                     MODCOL_FILETYPE,         SPECIAL_ITEM_NAME,
01601                     MODCOL_FILETYPE_DISPLAY, "",
01602                     MODCOL_DIRECTORY_PATH,   path,
01603                     MODCOL_IS_DIRECTORY,     TRUE,
01604                     MODCOL_FILESIZE,         "",
01605                     MODCOL_FILEDATE,         "",
01606                     MODCOL_SUBTITLE,           subtitle,
01607                     MODCOL_THUMBNAIL,        get_icon_from_file(iconname, g_thumbnail_size),
01608                     MODCOL_TOGGLED,          -1,
01609                     -1 );
01610     desktop_names = g_slist_append(desktop_names, g_strdup(filename));
01611 }
01612 
01613 
01614 #if MACHINE_IS_DR800S || MACHINE_IS_DR800SG || MACHINE_IS_DR800SW
01615 static gboolean add_desktop_last_read_item(int index)
01616 {
01617     if (!g_last_read) return FALSE;
01618 
01619     g_assert(g_last_read->filename);
01620     g_assert(g_last_read->filetype);
01621     g_assert(g_last_read->directory_path);
01622     g_assert(g_last_read->filename_display);
01623 
01624     if (open_global_database(DIR_LIBRARY) != ER_OK)
01625     {
01626         ERRORPRINTF("cannot open database on card");
01627         return FALSE;
01628     }
01629 
01630     int rc = create_query();
01631     if (rc != ER_OK) return FALSE;
01632 
01633     if (g_last_read_results) {
01634         metadata_table_free(g_last_read_results);
01635         g_last_read_results = NULL;
01636     }
01637     rc = db_query_get_metadata(g_last_read->filename->str, g_last_read->directory_path->str, &g_last_read_results);
01638     if (rc != ER_OK) {
01639         ERRORPRINTF("cannot get metadata for database");
01640         return FALSE;
01641     }
01642 
01643     
01644     if (g_last_read_results)
01645     {
01646         if (!desktop_item_visible(index)) return TRUE;
01647 
01648         const metadata_cell *cell = (const metadata_cell*) (g_last_read_results->cell_data->data);
01649         const char *title = get_row_title(cell);
01650 
01651         
01652         int thumbsize = 0;
01653         const guchar* thumbdata = get_row_thumbnail(cell, &thumbsize);
01654         GdkPixbuf *thumbnail = NULL;
01655         if (thumbdata != NULL) {
01656             thumbnail = get_thumbnail_from_data(thumbdata, thumbsize, g_last_read->filename->str);
01657         } else {
01658             thumbnail = get_icon_from_file_extension(g_last_read->filetype->str, FALSE, g_thumbnail_size);
01659             if (thumbnail) g_object_ref(thumbnail);
01660         }
01661 
01662         gtk_list_store_insert_with_values(
01663                         g_filestore,
01664                         NULL,
01665                         g_items_per_page,
01666                         MODCOL_FILENAME,         g_last_read->filename->str,
01667                         MODCOL_TITLE,            _("Continue Reading"),
01668                         MODCOL_FILETYPE,         g_last_read->filetype->str,
01669                         MODCOL_FILETYPE_DISPLAY, "",
01670                         MODCOL_DIRECTORY_PATH,   g_last_read->directory_path->str,
01671                         MODCOL_IS_DIRECTORY,     FALSE,
01672                         MODCOL_FILESIZE,         "",
01673                         MODCOL_FILEDATE,         "",
01674                         MODCOL_SUBTITLE,           title,
01675                         MODCOL_THUMBNAIL,        thumbnail,
01676                         MODCOL_TOGGLED,          -1,
01677                         -1 );
01678         if (thumbnail) g_object_unref(thumbnail);
01679         desktop_names = g_slist_append(desktop_names, g_strdup(g_last_read->filename->str));
01680         return TRUE;
01681     } else {
01682         
01683         clear_last_read();
01684         return FALSE;
01685     }
01686 }
01687 #endif
01688 
01689 
01690 #if MACHINE_IS_DR800SG || MACHINE_IS_DR800SW
01691 static void add_desktop_store_item(int index)
01692 {
01693     if (!desktop_item_visible(index)) return;
01694     
01695     const char* filename = "ebookmall.desktop";
01696     gtk_list_store_insert_with_values(
01697                     g_filestore,
01698                     NULL,
01699                     g_items_per_page,
01700                     MODCOL_FILENAME,         filename,
01701                     MODCOL_TITLE,            _("eBook Mall"),
01702                     MODCOL_FILETYPE,         "desktop",
01703                     MODCOL_FILETYPE_DISPLAY, "",
01704                     MODCOL_DIRECTORY_PATH,   DIR_DESKTOP_TEMPLATE,
01705                     MODCOL_IS_DIRECTORY,     FALSE,
01706                     MODCOL_FILESIZE,         "",
01707                     MODCOL_FILEDATE,         "",
01708                     MODCOL_SUBTITLE,           _("Browse the eBook Mall for new books and much more"),
01709                     MODCOL_THUMBNAIL,        get_icon_from_file("shop", g_thumbnail_size),
01710                     MODCOL_TOGGLED,          -1,
01711                     -1 );
01712     desktop_names = g_slist_append(desktop_names, g_strdup(filename));
01713 }
01714 #endif
01715 
01716 
01717 static void clear_desktop_names()
01718 {
01719     GSList *iter = desktop_names;
01720     while (iter) {
01721         g_free((char*)iter->data);
01722         iter = g_slist_next(iter);
01723     }
01724     g_slist_free(desktop_names);
01725     desktop_names = NULL;
01726 }
01727 
01728 
01729 static int add_desktop_items()
01730 {
01731     clear_desktop_names();
01732 
01733     const gchar* mountpoint = ipc_get_media();
01734     
01735     int idx = 0;
01736 #if MACHINE_IS_DR800S || MACHINE_IS_DR800SG || MACHINE_IS_DR800SW
01737     if (add_desktop_last_read_item(idx)) idx++;
01738 #endif
01739     add_desktop_item(idx++, SPECIAL_BOOKS, _("Books"), _("See all the books that have been saved to your library"), mountpoint, "books");
01740     add_desktop_item(idx++, SPECIAL_NEWS, _("News"), _("Read the latest newspapers and other periodicals"), mountpoint, "news");
01741     add_desktop_item(idx++, SPECIAL_IMAGES, _("Images"), _("View your saved pictures and images"), mountpoint, "images");
01742 #if MACHINE_IS_DR800SG || MACHINE_IS_DR800SW
01743     add_desktop_store_item(idx++);
01744 #endif
01745     add_desktop_item(idx++, SPECIAL_PERSONAL, _("Personal Documents"), _("See all the files saved in Personal Documents"), mountpoint, "personal");
01746     char buffer[512];
01747     sprintf(buffer, "%s/%s", mountpoint, DIR_NOTES);
01748     add_desktop_item(idx++, SPECIAL_NOTES, _("Notes"), _("Keep track of your notes with this handy notepad"), buffer, "notes");
01749     add_desktop_item(idx++, SPECIAL_RECENT, _("Recently Added"), _("View the last 15 items added to the device"), mountpoint, "recent");
01750 #if MACHINE_IS_DR1000S || MACHINE_IS_DR1000SW
01751     sprintf(buffer, "%s/%s", mountpoint, DIR_SHORTCUTS);
01752     add_desktop_item(idx++, SPECIAL_SHORTCUTS, _("Shortcuts"), _("Shortcuts that you've made to your documents are found here"), buffer, "shortcuts");
01753 #endif
01754     if (filetypes_showdir())
01755         add_desktop_item(idx++, SPECIAL_DIR, _("SD Card"), _("Browse and navigate the folders saved on the SD card"), mountpoint, "sdcard");
01756     add_desktop_item(idx++, SPECIAL_SEARCH, _("Search"), _("Search for books, news items, or images"), mountpoint, "search");
01757     add_desktop_item(idx++, SPECIAL_HELP, _("Help"), _("Learn more about the device and features"), mountpoint, "help");
01758     add_desktop_item(idx++, SPECIAL_SETTINGS, _("Settings"), _("Find and edit the settings used on this device"), "/usr/share/ctb/settings", "settings");
01759     return idx;
01760 }
01761 
01762 
01763 static GdkPixbuf *get_thumbnail(const metadata_cell *cell)
01764 {
01765     const char *filename = get_row_filename(cell);
01766     gboolean is_directory = get_row_is_directory(cell);
01767     const char *filetype = get_row_filetype(cell);
01768 
01769     
01770     int thumbsize = 0;
01771     const guchar* thumbdata = get_row_thumbnail(cell, &thumbsize);
01772     GdkPixbuf *thumbnail = NULL;
01773     if (thumbdata != NULL) {
01774         thumbnail = get_thumbnail_from_data(thumbdata, thumbsize, filename);
01775     }
01776 
01777     
01778     if (thumbnail == NULL && is_shortcut_file_extension(filetype)) {
01779         shortcut_t *shortcut = NULL;
01780         const char *dirpath = get_row_dirpath(cell);
01781         const gchar *dir = dirpath;
01782         if (strcmp(dirpath, ".") == 0) dir = g_current_dir;
01783         parse_shortcut_file(dir, filename, &shortcut);
01784         if (shortcut) {
01785             thumbnail = get_thumbnail_for_shortcut(shortcut, is_directory,
01786                     filetype, filename, dir);
01787             shortcut_free(shortcut);
01788         }
01789     }
01790 
01791     
01792     if (thumbnail == NULL) {
01793         thumbnail = get_icon_from_file_extension(filetype, is_directory,
01794                     g_thumbnail_size);
01795         if (thumbnail) g_object_ref(thumbnail);
01796     }
01797 
01798     return thumbnail;
01799 }
01800 
01801 
01802 static GdkPixbuf *create_delete_overlay(const GdkPixbuf* source, gboolean toggled)
01803 {
01804     GdkPixbuf *result = NULL;
01805     if (gdk_pixbuf_get_width(source) != 120 || gdk_pixbuf_get_height(source) != 120) {
01806         
01807         GdkPixbuf * empty = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 120, 120);
01808         gdk_pixbuf_fill(empty, 0x00000000);
01809         int x_offset = (120 - gdk_pixbuf_get_width(source)) / 2;
01810         int y_offset = (120 - gdk_pixbuf_get_height(source)) / 2;
01811         gdk_pixbuf_copy_area(source,
01812                              0, 0,      
01813                              gdk_pixbuf_get_width(source),   
01814                              gdk_pixbuf_get_height(source),  
01815                              empty,
01816                              x_offset, y_offset);    
01817         apply_icon_overlay_delete(MODTHUMB_MEDIUM, empty, toggled);
01818         result = empty;
01819     } else {
01820         GdkPixbuf *source_copy = gdk_pixbuf_copy(source);
01821         apply_icon_overlay_delete(MODTHUMB_MEDIUM, source_copy, toggled);
01822         return source_copy;
01823     }
01824     return result;
01825 }
01826 
01827 
01828 static char* get_title(const shortcut_t *shortcut, const char* title, const char* filename)
01829 {
01830     
01831     if (g_viewmode2 == DIR_VIEW) return g_strdup(filename);
01832 
01833     if (title && title[0]) return g_strdup(title);
01834 
01835     if (shortcut) return g_strdup(shortcut->name);
01836 
01837     return g_strdup(filename);
01838 }
01839 
01840 
01841 static char* get_subtitle(const shortcut_t *shortcut, const char* author)
01842 {
01843     if (!shortcut) {
01844         if (author && author[0]) {
01845             
01846 
01847             return g_strdup_printf(_("by %s"), author);
01848         } else {
01849             return NULL;
01850         }
01851     }
01852 
01853     if (g_viewmode2 == DIR_VIEW) return g_strdup(shortcut->name);
01854 
01855     if (shortcut->comment) return g_strdup(shortcut->comment);
01856 
01857     if (shortcut->type == SHORTCUT_TO_APPLICATION) return NULL;
01858 
01859     if (author) {
01860         
01861 
01862         return g_strdup_printf(_("shortcut to %s"), author);
01863     }
01864 
01865     return NULL;
01866 }
01867 
01868 
01869 
01870 
01871 static void load_items_in_model()
01872 {
01873     LOGPRINTF( "total=%d  offset=%d  items_per_page=%d",
01874                g_total_items, g_item_offset, g_items_per_page);
01875 
01876     if (g_values == NULL && g_total_items != 0) return;
01877     if (g_current_dir == NULL) return;
01878 
01879 #if (TIMING_ON)
01880     struct timeval t1, t2;
01881     gettimeofday(&t1, NULL);
01882 #endif
01883 
01884     
01885     gtk_list_store_clear(g_filestore);
01886 
01887     if (filemodel_current_is_desktop()) {
01888         const gchar* mountpoint = ipc_get_media();
01889         if (mountpoint == NULL) goto out;
01890         g_desktop_items = add_desktop_items();
01891         if (g_desktop_offset >= g_desktop_items) {
01892             
01893             gtk_list_store_clear(g_filestore);
01894             g_desktop_offset = 0;
01895             g_desktop_items = add_desktop_items();
01896         }
01897         update_numpages(g_items_per_page);
01898         goto out;
01899     }
01900 
01901     g_assert(g_item_offset <= g_total_items);
01902 
01903     int items_per_page = num_items_per_page();
01904     update_numpages(items_per_page);
01905     add_special_items();
01906     if (g_total_items == 0) goto out;
01907 
01908     
01909     const metadata_cell *cell = (const metadata_cell*) (g_values->cell_data->data);
01910 
01911     
01912     cell += (g_item_offset * g_values->n_columns);
01913 
01914     int row;
01915     for (row=g_item_offset; row < (g_item_offset + items_per_page) && row < g_total_items ; row++)
01916     {
01917         const char *filename = get_row_filename(cell);
01918         const char *dirpath = get_row_dirpath(cell);
01919         gboolean is_directory = get_row_is_directory(cell);
01920         const char *filetype = get_row_filetype(cell);
01921         const char *title = get_row_title(cell);
01922         const char *author = get_row_author(cell);
01923 
01924         const char *filetype_display = "";
01925         if (filetype) {
01926             filetype_display = get_type_descr_from_file_extension(filetype, is_directory);
01927         }
01928 
01929         gint64 size = get_row_filesize(cell);
01930         gchar *filesize = NULL;
01931         if (!is_directory) filesize = format_size(size);
01932 
01933         time_t date = get_row_filedate(cell);
01934         char filedate[100];
01935         filedate[0] = '\0';
01936         
01937 
01938 
01939         
01940         strftime(filedate, sizeof(filedate), _("%x %X"), localtime(&date));  
01941 
01942         gboolean is_shortcut = is_shortcut_file_extension(filetype);
01943 
01944         shortcut_t *shortcut = NULL;
01945         if (is_shortcut) {
01946             const gchar *dir = dirpath;
01947             if (strcmp(dirpath, ".") == 0) dir = g_current_dir;
01948             parse_shortcut_file(dir, filename, &shortcut);
01949         }
01950 
01951         
01952         char *title_display = get_title(shortcut, title, filename);
01953 
01954         
01955         gchar* subtitle_display = NULL;
01956         if (is_shortcut && shortcut == NULL) {
01957             subtitle_display = g_strdup(_("broken shortcut"));
01958         } else {
01959 #if MACHINE_HAS_ACSM
01960             if (strcmp("acsm", filetype) == 0) {
01961                 subtitle_display = g_strdup(_("This file will be downloaded to your device."));
01962             } else
01963 #endif
01964             {
01965                 subtitle_display = get_subtitle(shortcut, author);
01966             }
01967         }
01968 
01969         shortcut_free(shortcut);
01970 
01971         gint toggled = is_row_toggled(row, filename, dirpath);
01972 
01973         GdkPixbuf *thumbnail = get_thumbnail(cell);
01974 
01975         
01976         if (g_viewmode == DELETE_MODE && g_thumbnail_size == MODTHUMB_MEDIUM && toggled != -1)
01977         {
01978             GdkPixbuf *thumbnail_delete = create_delete_overlay(thumbnail, toggled);
01979             g_object_unref(thumbnail);
01980             thumbnail = thumbnail_delete;
01981         }
01982 
01983         
01984         gtk_list_store_insert_with_values( g_filestore,
01985                                            NULL,
01986                                            g_items_per_page,
01987                                            MODCOL_FILENAME,         filename,
01988                                            MODCOL_TITLE,            title_display,
01989                                            MODCOL_FILETYPE,         filetype,
01990                                            MODCOL_FILETYPE_DISPLAY, filetype_display,
01991                                            MODCOL_DIRECTORY_PATH,   dirpath,
01992                                            MODCOL_IS_DIRECTORY,     is_directory,
01993                                            MODCOL_FILESIZE,         filesize,
01994                                            MODCOL_FILEDATE,         filedate,
01995                                            MODCOL_SUBTITLE,         subtitle_display,
01996                                            MODCOL_THUMBNAIL,        thumbnail,
01997                                            MODCOL_TOGGLED,          toggled,
01998                                            -1 );
01999 
02000         if (thumbnail) g_object_unref(thumbnail);
02001         g_free(filesize);
02002         g_free(title_display);
02003         g_free(subtitle_display);
02004         cell += g_values->n_columns;
02005     }
02006 out:
02007 #if (TIMING_ON)
02008     gettimeofday(&t2, NULL);
02009     float cost = (float) (((t2.tv_sec - t1.tv_sec) * 1000 * 1000) + (t2.tv_usec - t1.tv_usec));
02010     printf("%s() duration=%4.1lf ms\n", __func__, cost/1000);
02011 #endif
02012     return; 
02013 }
02014 
02015 
02016 
02017 static gchar* format_size(const gint64 bytes)
02018 {
02019     gchar       *size = NULL;   
02020     long        n_int;
02021     long        n_frac;
02022 
02023     const int   MB = 1024 * 1024;
02024     const int   KB = 1024;
02025 
02026     if (bytes >= MB)
02027     {
02028         if (bytes >= 10 * MB)
02029         {
02030             n_int = (bytes + (MB / 2)) / MB;                    
02031             size = g_strdup_printf( "%ld %s",
02032                                      n_int,
02033                                           _("MB") );
02034         }
02035         else
02036         {
02037             n_int  = bytes / MB;                                
02038             n_frac = ((bytes % MB) + (MB / 20)) / (MB / 10);    
02039             n_frac = MIN(n_frac, 9);
02040             size = g_strdup_printf( "%ld.%ld %s",
02041                                      n_int,
02042                                          n_frac,
02043                                              _("MB") );
02044         }
02045     }
02046     else if (bytes >= KB)
02047     {
02048         if (bytes >= 10 * KB)
02049         {
02050             n_int = (bytes + (KB / 2)) / KB;                    
02051             size = g_strdup_printf( "%ld %s",
02052                                      n_int,
02053                                           _("KB") );
02054         }
02055         else
02056         {
02057             n_int  = bytes / KB;                                
02058             n_frac = ((bytes % KB) + (KB / 20)) / (KB / 10);    
02059             n_frac = MIN(n_frac, 9);
02060             size = g_strdup_printf( "%ld.%ld %s",
02061                                      n_int,
02062                                          n_frac,
02063                                              _("KB") );
02064         }
02065     }
02066     else
02067     {
02068         size = g_strdup_printf( "%lld %s",
02069                                  bytes,
02070                                     _("B") );
02071     }
02072 
02073     return size;
02074 }
02075 
02076 
02077 
02078 
02079 
02080 
02081 filelist_entry_t* iter_to_entry(GtkTreeModel *model, GtkTreeIter *iter)
02082 {
02083     gchar    *filename         = NULL;
02084     gchar    *filename_display = NULL;
02085     gchar    *filetype         = NULL;
02086     gchar    *directory_path   = NULL;
02087     gboolean is_directory      = FALSE;
02088 
02089     gtk_tree_model_get( model, iter, MODCOL_FILENAME,         &filename,
02090                                      MODCOL_TITLE, &filename_display,
02091                                      MODCOL_FILETYPE,         &filetype,
02092                                      MODCOL_DIRECTORY_PATH,   &directory_path,
02093                                      MODCOL_IS_DIRECTORY,     &is_directory,
02094                                      -1 );
02095 
02096     filelist_entry_t *entry = filelist_entry_new();
02097     entry->filename         = g_string_new(filename);
02098     entry->filename_display = g_string_new(filename_display);
02099     entry->filetype         = g_string_new(filetype);
02100     entry->directory_path   = g_string_new(directory_path);
02101     entry->is_directory     = is_directory;
02102 
02103     g_free(filename);
02104     g_free(filename_display);
02105     g_free(filetype);
02106     g_free(directory_path);
02107 
02108     return entry;
02109 }
02110 
02111 
02112 filelist_entry_t* filelist_entry_new()
02113 {
02114     filelist_entry_t *thiz = g_new0(filelist_entry_t, 1);
02115     g_assert(thiz);
02116     thiz->is_directory = FALSE;
02117 
02118     return thiz;
02119 }
02120 
02121 
02122 void filelist_entry_free(filelist_entry_t *thiz)
02123 {
02124     if (thiz)
02125     {
02126         if (thiz->filename) g_string_free(thiz->filename, TRUE);
02127         if (thiz->filename_display) g_string_free(thiz->filename_display, TRUE);
02128         if (thiz->filetype) g_string_free(thiz->filetype, TRUE);
02129         if (thiz->directory_path) g_string_free(thiz->directory_path, TRUE);
02130         g_free(thiz);
02131     }
02132 }
02133 
02134 
02135 filelist_entry_t* filelist_entry_copy ( const filelist_entry_t *src )
02136 {
02137 
02138     g_assert(src);
02139     g_assert(src->filename         && src->filename->str);
02140     g_assert(src->filename_display && src->filename_display->str);
02141     g_assert(src->filetype         && src->filetype->str);
02142     g_assert(src->directory_path   && src->directory_path->str);
02143 
02144     filelist_entry_t *thiz = filelist_entry_new();
02145     thiz->filename = g_string_new(src->filename->str);
02146     thiz->directory_path = g_string_new(src->directory_path->str);
02147     thiz->filename_display = g_string_new(src->filename_display->str);
02148     thiz->filetype = g_string_new(src->filetype->str);
02149     thiz->is_directory = src->is_directory;
02150 
02151     return thiz;
02152 }
02153 
02154 
02155 #if MACHINE_IS_DR800S || MACHINE_IS_DR800SG || MACHINE_IS_DR800SW
02156 
02157 static gboolean filelist_entry_equal(const filelist_entry_t *left,
02158                                      const filelist_entry_t *right)
02159 {
02160     if (left == NULL || right == NULL) return FALSE;
02161     if (strcmp(left->filename->str, right->filename->str) != 0) return FALSE;
02162     if (strcmp(left->directory_path->str, right->directory_path->str) != 0) return FALSE;
02163     return TRUE;
02164 }
02165 
02166 
02167 void filemodel_update_last_read(const filelist_entry_t *fileinfo)
02168 {
02169     
02170     if (fileinfo->directory_path->str[0] == 0) return;
02171 
02172     if (!filelist_entry_equal(g_last_read, fileinfo)) {
02173         clear_last_read();
02174         g_last_read = filelist_entry_copy(fileinfo);
02175 
02176         time_t now = time(NULL);
02177         int rc = db_query_update_lastread(fileinfo->filename, fileinfo->directory_path, now);
02178         if (rc != ER_OK) ERRORPRINTF("cannot update database");
02179     }
02180 }
02181 #endif
02182 
02183 
02184 #if (TIMING_ON)
02185 static u_int64_t get_time_now()
02186 {
02187     struct timespec now;
02188     clock_gettime(CLOCK_MONOTONIC, &now);
02189     u_int64_t now64 = now.tv_sec;
02190     now64 *= 1000000;
02191     now64 += (now.tv_nsec/1000);
02192     return now64;
02193 }
02194 
02195 static u_int64_t t1 = 0;
02196 static u_int64_t t2 = 0;
02197 
02198 void start_duration_timer()
02199 {
02200     LOGPRINTF("");
02201     if (t1 == 0) t1 = get_time_now();
02202 }
02203 
02204 
02205 void stop_duration_timer()
02206 {
02207     if (t1 != 0) {
02208         t2 = get_time_now();
02209         float duration = t2 - t1;
02210         printf("ACTION DURATION = %4.1lf ms\n\n", duration / 1000);
02211         t1 = 0;
02212     }
02213 }
02214 #endif
02215