filemodel.c

Go to the documentation of this file.
00001 /*
00002  * File Name: filemodel.c
00003  */
00004 
00005 /*
00006  * This file is part of ctb.
00007  *
00008  * ctb is free software: you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation, either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * ctb is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program. If not, see <http://www.gnu.org/licenses/>.
00020  */
00021  
00022 /**
00023  * Copyright (C) 2008 iRex Technologies B.V.
00024  * All rights reserved.
00025  */
00026 
00027 //----------------------------------------------------------------------------
00028 // Include Files
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 // Type Declarations
00053 //----------------------------------------------------------------------------
00054 
00055 static const char* order_names[N_CTB_SORT_ORDER] =
00056         {
00057                 // TRANSLATORS: this is a document sorting option
00058                 [CTB_SORT_BY_NAME]       = N_("- by title"),
00059                 // TRANSLATORS: this is a document sorting option
00060                 [CTB_SORT_BY_TYPE]       = N_("- by type"),
00061                 // TRANSLATORS: this is a document sorting option
00062                 [CTB_SORT_BY_SIZE]       = N_("- by size"),
00063                 // TRANSLATORS: this is a document sorting option
00064                 [CTB_SORT_BY_DATE_ADDED] = N_("- by date added"),
00065                 // TRANSLATORS: this is a document sorting option
00066                 [CTB_SORT_BY_DATE_READ]  = N_("- recently opened"),
00067                 // TRANSLATORS: this is a document sorting option
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     {   // view,             title,               tag_filter,      default sort,          fixed,
00094         //                                                          order                  order
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 // Constants
00111 //----------------------------------------------------------------------------
00112 
00113 static const char *THUMBNAIL_IMAGE_TYPE = "png";
00114 static int NUM_RECENT_ITEMS = 15;
00115 
00116 
00117 //----------------------------------------------------------------------------
00118 // Static Variables
00119 //----------------------------------------------------------------------------
00120 
00121 static GtkListStore          *g_filestore    = NULL;            // details of files
00122 static gint                  g_items_per_page     = 1;               // max. number of items to be displayed
00123 static gint                  g_item_offset   = 0;               // offset of first item to be displayed
00124 static gint                  g_total_items   = 0;               // total number of items
00125 static gboolean              g_has_next_page = FALSE;           // do we have a next page to be displayed?
00126 static int                   g_curpage       = 0;               // shown in pagecounter
00127 static int                   g_numpages      = 0;               // shown in pagecounter
00128 static gboolean              g_boundary_check = TRUE;           // on TRUE greys out nav. arrows, FALSE for reflowable docs
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;            // item sorting order ascending or descending
00135 static filemodel_thumbsize_t g_thumbnail_size= MODTHUMB_MEDIUM; // thumbail size to retrieve from database
00136 
00137 static gchar                 *g_current_dir  = NULL;            // current directory
00138 static metadata_table        *g_values       = NULL;            // results from metadb
00139 static GSList                *toggled_list   = NULL;            // list of toggled items
00140 static gchar                 *g_delete_text  = NULL;            // text shown on first item in delete mode
00141 static time_t                g_last_modified = 0;               // when global.db was last modified
00142 
00143 static GList                 *g_dir_stack    = NULL;            // stack of directories
00144 // value 0 = not active, 1 has dir with initial, 2 has dir with initial, 3 has both
00145 static char g_alpha_list[FILEMODEL_NUM_ALPHA]; // [0='#', 1='a', 26='z']
00146 static GSList                *desktop_names  = NULL;            // names of visible desktop items
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 // init: when ctb is started its window is on top
00157 static gboolean g_window_is_on_top = TRUE;                      // needed for page bar updates
00158 static ViewMode              g_viewmode2 = -1;
00159 
00160 //============================================================================
00161 // Local Function Definitions
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 // Functions Implementation
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; // first item already
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;        // see create_query()
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;        // see create_query()
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;        // see create_query()
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;        // see create_query()
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;        // see create_query()
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;        // see create_query()
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;        // see create_query()
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;        // see create_query()
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     // NOTE if order is changed, change get_row_xxx() methods too!
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 // NOTE g_values can be zero afterwards if no items present
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     // cleanup old results
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             // already handled
00439             break;
00440         case SETTINGS_VIEW:
00441             // get normal view (settings)
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             // get tag-filtered view (Books, Images, News, Notes)
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             // get directory-filtered view (SD Card)
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             // get directory-filtered view (SD Card)
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             // search results (when user searches)
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     // note: values is NULL when no files/folders selected
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     // depends on viewmode
00551     if (g_viewmode2 == DESKTOP_VIEW || g_viewmode2 == SETTINGS_VIEW) return FALSE;
00552 
00553     // otherwise depend on sortorder
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     // round g_desktop_offset down to new pagination
00644     int desktop_page = g_desktop_offset / g_items_per_page;
00645     g_desktop_offset = desktop_page * g_items_per_page;
00646 
00647     // round g_item_offset down to new pagination
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     // NOTE: 'fixes' wrong height in contentview
00680     gtk_list_store_clear(g_filestore);
00681 }
00682 
00683 
00684 // returns number of shown items (NOT special items like Up, etc) per page
00685 // NOTE: the minimum is 1
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');  // normal letter
00797     } else {
00798         return 0;   // index for '#'
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) ||  // normal char (a-z)
00820                 (letter == '#' && !isalpha(current))) {             // special char (0-9,!,?, etc)
00821                 // check whether it's the preferred type
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         // shouldn't really happen!
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     // special for desktop
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         // skip g_item_offset rows
00894         cell += (g_item_offset * g_values->n_columns);
00895         // search within currently visible window
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 // loads items from metadata.db and load them into model for GUI
00915 // the dir-stack has already been modified for new dir
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         // do nothing
00925     } else {
00926         if (g_viewmode2 == SETTINGS_VIEW) {
00927             ret = open_global_database(dir);    // Settings
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         // still can go up more
01010         filemodel_chdir(entry->parent_dir, entry->last_item);
01011     }
01012     else
01013     {
01014         // on desktop
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     // set new offset as close to old one as possible
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     // load pixbuf with data from blob
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     // get a pointer to the resulting pixbuf, if any
01069     thumbnail = gdk_pixbuf_loader_get_pixbuf(thumb_ldr);
01070     if (thumbnail) {
01071         // take a reference on the object
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         // check if allowed
01152         if (!is_delete_allowed(filename, dirpath)) return -1;
01153 
01154         // check if we have previous state
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     // add overlay for delete mode in iconView
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     // skip g_item_offset rows
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     // check last_read item
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) { // if none toggled do nothing
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     // dont allow System dir to be deleted
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         // not allowed to delete items from internal desktop
01295         if (strcmp(real_dir, DIR_DESKTOP_INTERNAL) == 0) return FALSE;
01296 
01297         // not allow to delete items outside mountpoint (Settings, etc)
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;       // Previous dir
01340         case NOTES_VIEW:
01341             return 2;       // Previous dir + New Note
01342         case DIR_VIEW:
01343         case SHORTCUT_VIEW:
01344             return 1;       // Up a level
01345     }
01346     return 0;       // for compiler warning
01347 }
01348 
01349 
01350 static void add_browse_mode_specials()
01351 {
01352     switch (g_viewmode2) {
01353         case DESKTOP_VIEW:
01354             // dont insert anything
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                             // NOTE should match filename in check for shortcut_allowed()
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 // depends on viewmode (browse/delete) and viewmode2
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 // sets g_curpage + g_numpages and reports to ipc
01442 // NOTE items_per_page does NOT incude special items (Up a dir, Delete, etc)
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         // NOTE: g_item offset must be multiple of items_per_page
01454         //       so fix if needed. Always round down
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;   // start at 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     // get default icon
01491     switch (shortcut->type)
01492     {
01493         case SHORTCUT_TO_FILE:
01494         {
01495             // get icon for extension of target file
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             // get icon for library
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                     // internal library root
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                         // path: /media/
01521                         cp = strchr( cp + n + 1, '/' );
01522                         if ( cp == NULL )
01523                         {
01524                             // external library root
01525                             thumbnail = get_icon_library( g_thumbnail_size );
01526                         }
01527                     }
01528                 }
01529             }
01530 
01531             // get icon for folder
01532             if (thumbnail == NULL)
01533             {
01534                 thumbnail = get_icon_from_file_extension( "",
01535                                                           TRUE,        // is_directory
01536                                                           g_thumbnail_size );
01537             }
01538             break;
01539         }
01540         case SHORTCUT_TO_APPLICATION:
01541             // get icon for application
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             // get icon from file extension
01556             thumbnail = get_icon_from_file_extension( filetype,
01557                                                       is_directory,
01558                                                       g_thumbnail_size );
01559     }
01560 
01561     // add shortcut overlay
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     // NOTE: values is NULL when no results
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         // get thumbnail
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         // entry doesn't exist anymore, remove last read entry
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     // add browser ('Shop') link
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     // try to read from metadata results
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     // special for shortcuts
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     // get default icon when no thumbnail available
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         // overlay on good image
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,      // src x,y
01813                              gdk_pixbuf_get_width(source),   // dest width
01814                              gdk_pixbuf_get_height(source),  // dest height
01815                              empty,
01816                              x_offset, y_offset);    // dest x,y
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     // in DIR_VIEW we show filenames
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             /* TRANSLATORS: %s in this string will be replaced by the author of the book.
01846                For example: 'by Charles Dickens' */
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         /* TRANSLATORS: %s in this string will be replaced by the filename.
01861            For Example: 'shortcut to files/foo/bar.pdf' */
01862         return g_strdup_printf(_("shortcut to %s"), author);
01863     }
01864 
01865     return NULL;
01866 }
01867 
01868 
01869 // retrieve items from g_values and load these in file model for GUI
01870 // it will start at g_item_offset
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     // remove old items from filestore
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             // items were removed while not on desktop, show page 0
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     // add new items to filestore
01909     const metadata_cell *cell = (const metadata_cell*) (g_values->cell_data->data);
01910 
01911     // skip g_item_offset rows
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         /* TRANSLATORS: This is a date and time format specifier, according to the strftime(3) man page.
01937            The result will be used to identify date and time of a file, e.g. '02/03/2010 11:13:01'.
01938            If in doubt, please keep unchanged. '%x %X' already takes the locale into account. */
01939         // xgettext:no-c-format
01940         strftime(filedate, sizeof(filedate), _("%x %X"), localtime(&date));  // xgettext:no-c-format
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         // title
01952         char *title_display = get_title(shortcut, title, filename);
01953 
01954         // subtitle
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         // add overlay for delete mode in iconView
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         // add to filestore
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; // needed to avoid empty label
02013 }
02014 
02015 
02016 // format filesize into a string
02017 static gchar* format_size(const gint64 bytes)
02018 {
02019     gchar       *size = NULL;   // return value
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;                    // MB round upward
02031             size = g_strdup_printf( "%ld %s",
02032                                      n_int,
02033                                           _("MB") );
02034         }
02035         else
02036         {
02037             n_int  = bytes / MB;                                //     MB round downward
02038             n_frac = ((bytes % MB) + (MB / 20)) / (MB / 10);    // 0.1 MB round upward
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;                    // KB round upward
02051             size = g_strdup_printf( "%ld %s",
02052                                      n_int,
02053                                           _("KB") );
02054         }
02055         else
02056         {
02057             n_int  = bytes / KB;                                //     KB round downward
02058             n_frac = ((bytes % KB) + (KB / 20)) / (KB / 10);    // 0.1 KB round upward
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 // Functions to manipulate data in filestore
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 // NOTE only compare dir + filename, since that's unique
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     // skip entries without directory (like new note)
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 
Generated by  doxygen 1.6.2-20100208