menustore.c

Go to the documentation of this file.
00001 /*
00002  * File Name: menustore.c
00003  */
00004 
00005 /*
00006  * This file is part of popupmenu.
00007  *
00008  * popupmenu 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  * popupmenu 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 // system include files, between < >
00032 #include <gtk/gtk.h>
00033 #include <string.h>
00034 
00035 // ereader include files, between < >
00036 #include <libergtk/ergtk.h>
00037 
00038 // local include files, between " "
00039 #include "log.h"
00040 #include "menustore.h"
00041 #include "pixlist.h"
00042 #include "i18n.h"
00043 #include "ipc.h"
00044 
00045 //----------------------------------------------------------------------------
00046 // Type Declarations
00047 //----------------------------------------------------------------------------
00048 
00049 enum menu_type
00050 {
00051   MENU_TYPE_GROUP = 0,
00052   MENU_TYPE_ITEM,
00053 };
00054 
00055 typedef struct
00056 {
00057     gchar* name;
00058     gchar* service;
00059     GList* groups;      // entries are gchar*
00060 } MenuEntry;
00061 
00062 typedef struct
00063 {
00064     const gchar* name;
00065     gboolean found;
00066     GtkTreeIter* iter;
00067 } SearchArgs;
00068 
00069 #if MACHINE_IS_DR1000S || MACHINE_IS_DR1000SW
00070 typedef struct
00071 {
00072     const char* pname;
00073     const char* iname;
00074 } ToolItem;
00075 
00076 typedef struct
00077 {
00078     const char* menu;
00079     ToolItem items[MENUSTORE_NUM_TOOLS];
00080 } MenuToolItems;
00081 #endif
00082 
00083 //----------------------------------------------------------------------------
00084 // Global Variables
00085 //----------------------------------------------------------------------------
00086 
00087 GtkTreeStore *menu_store = NULL;
00088 static GList *menu_list = NULL; // entries are MenuEntry*
00089 static gchar *current_menu = NULL;
00090 static taskbar_cb_t taskbar_home_cb = NULL;
00091 static gboolean g_popup_has_changed = FALSE;
00092 static gboolean g_toolbar_has_changed = FALSE;
00093 
00094 
00095 //============================================================================
00096 // Functions Implementation
00097 //============================================================================
00098 
00099 #if MACHINE_IS_DR1000S || MACHINE_IS_DR1000SW
00100 static MenuToolItems hardcoded_tools[] =
00101 {
00102     { "ctb_menu_content", {     // desktop + settings
00103         { "system_top", "desktop" },
00104         { "ctb_view", "view_small" },
00105         { "ctb_view", "view_detail" },
00106         { "ctb_view", "view_content" },
00107     } },
00108     { "ctb_menu_content_media", {   // in Folder, except recent view
00109         { "system_top", "desktop" },
00110         { "ctb_view", "view_small" },
00111         { "ctb_view", "view_detail" },
00112         { "ctb_view", "view_content" },
00113     } },
00114     { "ctb_menu_recent_mode", {   // in Recent view
00115         { "system_top", "desktop" },
00116         { "ctb_view", "view_small" },
00117         { "ctb_view", "view_detail" },
00118         { "ctb_view", "view_content" },
00119     } },
00120     { "ctb_menu_delete_mode", {   // in Delete Mode
00121         { "system_top", "desktop" },
00122         { "ctb_view", "view_small" },
00123         { "ctb_view", "view_detail" },
00124         { "ctb_view", "view_content" },
00125     } },
00126     { "uds_menu_reading", {     // normal reading mode
00127         { "system_top", "desktop" },
00128         { "system_top", "back_to_library" },
00129         { "uds_navigation", "search_text" },
00130         { "uds_navigation", "goto_page" },
00131         { "pen_functions", "pen" },
00132         { "pen_functions", "eraser" },
00133         { "zoom_page", "mode_continuous" },
00134         { "zoom_shift", "zoom_fit" },
00135         { "zoom_shift", "zoom_selection" },
00136         { "zoom_shift", "mode_pan" },
00137         { "uds_views", "view_annotation" },
00138         { "uds_views", "view_toc" },
00139         { "uds_views", "view_thumbnail" },
00140         { "uds_navigation", "close" },
00141     } },
00142     { "uds_menu_annotation", {     // bookmarks view, has pagecounter!!
00143         { "system_top", "desktop" },
00144         { "system_top", "back_to_library" },
00145         { "uds_views", "view_reading" },
00146         { "uds_views", "view_toc" },
00147         { "uds_views", "view_thumbnail" },
00148     } },
00149     { "uds_menu_toc", {           // TableOfContent mode, has pagecounter!!
00150         { "system_top", "desktop" },
00151         { "system_top", "back_to_library" },
00152         { "uds_views", "view_reading" },
00153         { "uds_views", "view_annotation" },
00154         { "uds_views", "view_thumbnail" },
00155     } },
00156     { "uds_menu_thumbnail", {     // Thumbnail view, has pagecounter!!
00157         { "system_top", "desktop" },
00158         { "system_top", "back_to_library" },
00159         { "uds_views", "view_reading" },
00160         { "uds_views", "view_annotation" },
00161         { "uds_views", "view_thumbnail" },
00162     } },
00163     { "uds_menu_bookinfo", {   
00164         { "system_top", "desktop" },
00165         { "system_top", "back_to_library" },
00166         { "uds_views", "view_reading" },
00167         { "uds_views", "view_annotation" },
00168         { "uds_views", "view_thumbnail" },
00169     } },
00170     { "notepad_menu", {
00171         { "system_top", "desktop" },
00172         { "notepad_actions", "clear_page" },
00173         { "notepad_actions", "insert_page" },
00174         { "notepad_actions", "delete_page" },
00175         { "notepad_actions", "rename" },
00176     } },
00177 };
00178 #endif
00179 
00180 const char* menustore_get_current_menu()
00181 {
00182     return current_menu;
00183 }
00184 
00185 
00186 void menustore_set_current_menu(const char* name)
00187 {
00188     if (current_menu && name && strcmp(name, current_menu) == 0) return;
00189 
00190     g_free(current_menu);
00191     if (name == NULL)  current_menu = NULL;
00192     else current_menu = g_strdup(name);
00193     g_popup_has_changed = TRUE;
00194     g_toolbar_has_changed = TRUE;
00195 }
00196 
00197 
00198 static void add_system_groups()
00199 {
00200     LOGPRINTF("entry");
00201     
00202     menustore_add_group("system_top", NULL, NULL, NULL);
00203     menustore_add_item( "desktop",         "system_top", _("Go to Home"),    "desktop");
00204     menustore_add_item( "back_to_library", "system_top", "", "back_to_library");
00205     menustore_set_item_state("back_to_library", "system_top", "disabled");
00206     
00207     menustore_add_group("system_bottom", NULL, NULL, NULL);
00208     menustore_add_item( "rotate_screen",   "system_bottom", _("Rotate"),        "rotate_screen");
00209 #if MACHINE_IS_DR1000S || MACHINE_IS_DR1000SW
00210     menustore_add_item( "lock",            "system_bottom", _("Lock Sensors"),  "lock");
00211     menustore_add_item( "eject_card",      "system_bottom", _("Safely Remove"), "eject_card");
00212     menustore_add_item( "shutdown",        "system_bottom", _("Turn Off Device"),"shutdown");
00213     // start with card unavailable
00214     menustore_set_item_state("eject_card", "system_bottom", "disabled");
00215 #endif    
00216 }
00217 
00218 
00219 void menustore_set_text()
00220 {
00221     LOGPRINTF("entry");
00222 
00223     // update language texts
00224     menustore_set_item_label("desktop",         "system_top",    _("Go to Home"));
00225     menustore_set_item_label("back_to_library", "system_top",    "");
00226 #if MACHINE_IS_DR1000S || MACHINE_IS_DR1000SW
00227     menustore_set_item_label("lock",            "system_bottom", _("Lock Sensors"));
00228     menustore_set_item_label("eject_card",      "system_bottom", _("Safely Remove"));
00229     menustore_set_item_label("shutdown",        "system_bottom", _("Turn Off Device"));
00230 #endif
00231     menustore_set_item_label("rotate_screen",   "system_bottom", _("Rotate"));
00232 }
00233 
00234 
00235 
00236 void menustore_create(taskbar_cb_t cb)
00237 {
00238     menu_store = gtk_tree_store_new (TREE_NUM_COLS,
00239                                      G_TYPE_INT,        // TREE_COL_TYPE
00240                                      G_TYPE_STRING,     // TREE_COL_NAME
00241                                      G_TYPE_STRING,     // TREE_COL_TEXT
00242                                      G_TYPE_STRING,     // TREE_COL_ICON
00243                                      G_TYPE_INT,        // TREE_COL_STATE
00244                                      GDK_TYPE_PIXBUF,   // TREE_COL_IMAGE_NORMAL
00245                                      GDK_TYPE_PIXBUF,   // TREE_COL_IMAGE_ALTERNATE
00246                                      G_TYPE_POINTER);   // TREE_COL_ITER
00247     taskbar_home_cb = cb;
00248     add_system_groups();
00249     menustore_set_text();
00250 }
00251 
00252 
00253 static char* type2str[] = {
00254   [MENU_TYPE_GROUP]   = "GROUP",
00255   [MENU_TYPE_ITEM]    = " ITEM",
00256 };
00257 
00258 
00259 static const char *state2str[] =
00260 {
00261     [MENU_STATE_NORMAL]    = "normal",
00262     [MENU_STATE_SELECTED]  = "selected",
00263     [MENU_STATE_DISABLED]  = "disabled",
00264     [MENU_STATE_ALTERNATE] = "alternate",
00265 };
00266 
00267 
00268 static enum menu_state string2state(const char* statestr)
00269 {
00270     if (g_ascii_strcasecmp(statestr, "normal") == 0) return MENU_STATE_NORMAL;
00271     if (g_ascii_strcasecmp(statestr, "selected") == 0) return MENU_STATE_SELECTED;
00272     if (g_ascii_strcasecmp(statestr, "disabled") == 0) return MENU_STATE_DISABLED;
00273     if (g_ascii_strcasecmp(statestr, "alternate") == 0) return MENU_STATE_ALTERNATE;
00274     ERRORPRINTF("unknown state: %s", statestr);
00275     return -1;
00276 }
00277 
00278 
00279 static void pixbuf_unref(GdkPixbuf *img)
00280 {
00281     if (img != NULL) g_object_unref(img);
00282 }
00283 
00284 
00285 #if (TESTING_ON)
00286 static int debug_count = 0;
00287 
00288 static gboolean printfunc(GtkTreeModel* model, GtkTreePath* path, GtkTreeIter* iter, gpointer data)
00289 {
00290     enum menu_type type = MENU_TYPE_ITEM;
00291     enum menu_state state = MENU_STATE_NORMAL;
00292     gchar *name = NULL;
00293     gchar *text = NULL;
00294     gchar *icon = NULL;
00295     GdkPixbuf *img_normal = NULL;
00296     GdkPixbuf *img_alternate = NULL;
00297 
00298     gtk_tree_model_get(model, iter,
00299                        TREE_COL_TYPE, &type,
00300                        TREE_COL_NAME, &name,
00301                        TREE_COL_TEXT, &text,
00302                        TREE_COL_ICON, &icon,
00303                        TREE_COL_STATE, &state,
00304                        TREE_COL_IMAGE_NORMAL, &img_normal,
00305                        TREE_COL_IMAGE_ALTERNATE, &img_alternate,
00306                        -1);
00307     int depth = gtk_tree_path_get_depth(path);
00308     char indent [20];
00309     memset(indent, ' ', sizeof(indent));
00310     indent[(depth-1)*3] = 0;
00311     gchar* pathstr = gtk_tree_path_to_string(path);
00312     printf("[%2d %s] %s%s '%s'   icon=%s  images=%p|%p  state=%s\n", ++debug_count, type2str[type],
00313             indent, name, text, icon, img_normal, img_alternate, state2str[state]);
00314 
00315     pixbuf_unref(img_normal);
00316     pixbuf_unref(img_alternate);
00317     g_free(name);
00318     g_free(text);
00319     g_free(icon);
00320     g_free(pathstr);
00321     return FALSE;
00322 }
00323 #endif
00324 
00325 
00326 static gboolean compare_func(GtkTreeModel* model, GtkTreePath* path, GtkTreeIter* iter, gpointer data)
00327 {
00328     SearchArgs* args = (SearchArgs*)data;
00329     gboolean stop = FALSE;
00330 
00331     gchar *name = NULL;
00332     GtkTreeIter* iter2;
00333     gtk_tree_model_get(model, iter,
00334                        TREE_COL_NAME, &name,
00335                        TREE_COL_ITER, &iter2,
00336                        -1);
00337     if (strcmp(name, args->name) == 0) {
00338         stop = TRUE;
00339         args->found = TRUE;
00340         args->iter = iter2;
00341     }
00342     
00343     g_free(name);
00344     return stop;
00345 }
00346 
00347 
00348 static gboolean name_exists(const char* name)
00349 {
00350     SearchArgs args;
00351     args.name = name;
00352     args.found = FALSE;
00353     args.iter = NULL;
00354     gtk_tree_model_foreach(GTK_TREE_MODEL(menu_store), compare_func, &args);
00355     return args.found;
00356 }
00357 
00358 
00359 static GtkTreeIter *get_iter(const char* name)
00360 {
00361     SearchArgs args;
00362     args.name = name;
00363     args.found = FALSE;
00364     args.iter = NULL;
00365     gtk_tree_model_foreach(GTK_TREE_MODEL(menu_store), compare_func, &args);
00366     if (!args.found) return NULL;
00367     return args.iter;
00368 }
00369 
00370 
00371 static GtkTreeIter* get_child_by_iter(const char* iname, GtkTreeIter *parent_iter)
00372 {
00373     GtkTreeIter child_iter;
00374     gboolean result = gtk_tree_model_iter_children(GTK_TREE_MODEL(menu_store), &child_iter, parent_iter);
00375     while (result == TRUE)
00376     {
00377         gboolean found = FALSE;
00378         gchar* child_name = NULL;
00379         GtkTreeIter* iter2;
00380         gtk_tree_model_get (GTK_TREE_MODEL(menu_store), &child_iter,
00381                             TREE_COL_NAME, &child_name,
00382                             TREE_COL_ITER, &iter2,
00383                             -1);
00384         if (strcmp(iname, child_name) == 0) found = TRUE; 
00385         g_free(child_name);
00386         if (found) return iter2;
00387         result = gtk_tree_model_iter_next(GTK_TREE_MODEL(menu_store), &child_iter);
00388     }
00389     return NULL;
00390 }
00391 
00392 
00393 static GtkTreeIter* get_child_by_name(const char* iname, const char* pname)
00394 {
00395     GtkTreeIter *parent_iter = NULL;
00396     if (pname) {
00397         // find parent
00398         parent_iter = get_iter(pname);
00399         if (!parent_iter) {
00400             WARNPRINTF("parent does not exist! (iname=%s, pname=%s)", iname, pname);
00401             return 0;
00402         }
00403     }
00404 
00405     return get_child_by_iter(iname, parent_iter);
00406 }
00407 
00408 
00409 gboolean menustore_add_group(const char *iname, const char *pname, const char *text, const char *icon)
00410 {
00411     TRACE("%s() iname=%s  pname=%s  text=%s\n", __func__, iname, pname, text);
00412 
00413     // group name should be unique!
00414     if (name_exists(iname)) {
00415         LOGPRINTF("name '%s' already used, re-adding", iname);
00416         menustore_remove_group(iname);
00417     }
00418 
00419     // find optional parent
00420     GtkTreeIter *parent_iter = NULL;
00421     if (pname != NULL && strlen(pname) > 0) {
00422         parent_iter = get_iter(pname);
00423         if (!parent_iter) {
00424             WARNPRINTF("parent does not exist! (iname=%s, pname=%s)", iname, pname);
00425             return FALSE;
00426         }
00427     }
00428 
00429     // add to store and list
00430     GtkTreeIter iter;
00431     gtk_tree_store_append (GTK_TREE_STORE(menu_store), &iter, parent_iter);
00432     gtk_tree_store_set (GTK_TREE_STORE(menu_store), &iter,
00433                       TREE_COL_TYPE, MENU_TYPE_GROUP,
00434                       TREE_COL_NAME, iname,
00435                       TREE_COL_TEXT, text,
00436                       TREE_COL_ICON, icon,
00437                       TREE_COL_STATE, MENU_STATE_NORMAL,
00438                       TREE_COL_IMAGE_NORMAL, pixlist_icon_state(icon, "normal"),
00439                       TREE_COL_ITER, gtk_tree_iter_copy(&iter),
00440                       -1);
00441     g_popup_has_changed = TRUE;
00442     g_toolbar_has_changed = TRUE;
00443     return TRUE;
00444 }
00445 
00446 
00447 gboolean menustore_add_item(const char *iname, const char *pname, const char *text, const char *icon)
00448 {
00449     TRACE("%s() iname=%s  pname=%s  text=%s\n", __func__, iname, pname, text);
00450 
00451     // check pname
00452     if (pname == 0 || strlen(pname) == 0) {
00453         WARNPRINTF("no pname (iname=%s)", iname);
00454         return FALSE;
00455     }
00456 
00457     // find parent
00458     GtkTreeIter *parent_iter = get_iter(pname);
00459     if (!parent_iter) {
00460         WARNPRINTF("parent does not exist! (iname=%s, pname=%s)", iname, pname);
00461         return FALSE;
00462     }
00463 
00464     // check if child exists
00465     if (get_child_by_iter(iname, parent_iter)) {
00466         ERRORPRINTF("name '%s.%s' already used", pname, iname);
00467         return FALSE;
00468     }
00469 
00470     // add to store and list
00471     GtkTreeIter iter;
00472     gtk_tree_store_append (GTK_TREE_STORE(menu_store), &iter, parent_iter);
00473     gtk_tree_store_set (GTK_TREE_STORE(menu_store), &iter,
00474                         TREE_COL_TYPE, MENU_TYPE_ITEM,
00475                         TREE_COL_NAME, iname,
00476                         TREE_COL_TEXT, text,
00477                         TREE_COL_ICON, icon,
00478                         TREE_COL_STATE, MENU_STATE_NORMAL,
00479                         TREE_COL_IMAGE_NORMAL, pixlist_icon_state(icon, "normal"),
00480                         TREE_COL_IMAGE_ALTERNATE, pixlist_icon_state(icon, "alternate"),
00481                         TREE_COL_ITER, gtk_tree_iter_copy(&iter),
00482                         -1);
00483     g_popup_has_changed = TRUE;
00484     g_toolbar_has_changed = TRUE;
00485     return TRUE;
00486 }
00487 
00488 
00489 static gboolean check_group(const char* group, const char* iname)
00490 {
00491     if (strlen(group) > 0 && !name_exists(group))
00492     {
00493         WARNPRINTF("cannot find group '%s' for item %s", group, iname);
00494         return FALSE;
00495     }
00496     return TRUE;
00497 }
00498 
00499 
00500 static void add_menu_group2(GList** list, const char* group)
00501 {
00502     if (strlen(group) != 0) {
00503        *list = g_list_prepend(*list, g_strdup(group));
00504     }
00505 }
00506 
00507 
00508 static MenuEntry* find_menu(const char* name)
00509 {
00510     if (name == NULL) return NULL;
00511 
00512     GList *iter = g_list_first(menu_list);
00513     while (iter != NULL)
00514     {
00515         MenuEntry *entry = (MenuEntry *) iter->data;
00516         if (strcmp(entry->name, name) == 0) return entry;
00517         iter = g_list_next(iter);
00518     }
00519     return NULL;
00520 }
00521 
00522 
00523 const char* menustore_get_current_service()
00524 {
00525     MenuEntry *entry = find_menu(current_menu);
00526     if (entry) return entry->service;
00527     return "";
00528 }
00529 
00530 
00531 gboolean menustore_add_menu(const char *iname, const char *ilabel, const char *service,
00532                             const char *group1,  const char *group2,
00533                             const char *group3, const char *group4)
00534 {
00535     TRACE("%s() iname=%s  ilabel=%s  service=%s  groups={%s, %s, %s, %s)\n", __func__,
00536         iname, ilabel, service, group1, group2, group3, group4);
00537 
00538     // check name
00539     if (find_menu(iname)) {
00540         LOGPRINTF("name '%s' already used, re-adding", iname);
00541         menustore_remove_menu(iname);
00542     }
00543 
00544     // check groups
00545     if (!check_group(group1, iname)) return FALSE;
00546     if (!check_group(group2, iname)) return FALSE;
00547     if (!check_group(group3, iname)) return FALSE;
00548     if (!check_group(group4, iname)) return FALSE;
00549 
00550     // add menu to list
00551     MenuEntry *new_menu = g_new0 (MenuEntry, 1);
00552     new_menu->name = g_strdup(iname);
00553     new_menu->service = g_strdup(service);
00554     new_menu->groups = NULL;
00555     menu_list = g_list_append(menu_list, new_menu);
00556 
00557     // add groups to menu
00558     add_menu_group2(&new_menu->groups, group1);
00559     add_menu_group2(&new_menu->groups, group2);
00560     add_menu_group2(&new_menu->groups, group3);
00561     add_menu_group2(&new_menu->groups, group4);
00562 
00563     g_popup_has_changed = TRUE;
00564     g_toolbar_has_changed = TRUE;
00565     return TRUE;
00566 }
00567 
00568 
00569 static gboolean check_type(GtkTreeIter* iter, const char* name, enum menu_type type)
00570 {
00571     enum menu_type realtype = MENU_TYPE_ITEM;
00572     gtk_tree_model_get (GTK_TREE_MODEL(menu_store), iter,
00573                         TREE_COL_TYPE, &realtype,
00574                         -1);
00575     if (realtype != type)
00576     {
00577         WARNPRINTF("'%s' is not of type %s", name, type2str[type]);
00578         return FALSE;
00579     }
00580     return TRUE;
00581 }
00582 
00583 
00584 gboolean menustore_remove_group(const char *name)
00585 {
00586     TRACE("%s() name=%s\n", __func__, name);
00587 
00588     // check name
00589     GtkTreeIter *menu_iter = get_iter(name);
00590     if (menu_iter == NULL)
00591     {
00592         LOGPRINTF("'%s' not found", name);
00593         return FALSE;
00594     }
00595 
00596     if (!check_type(menu_iter, name, MENU_TYPE_GROUP)) return FALSE;
00597 
00598     // remove children in this group and below
00599     GtkTreeIter child_iter;
00600     if (gtk_tree_model_iter_children(GTK_TREE_MODEL(menu_store), &child_iter, menu_iter))
00601     {
00602         enum menu_type type = MENU_TYPE_ITEM;
00603         gchar* child_name = NULL;
00604         gtk_tree_model_get (GTK_TREE_MODEL(menu_store), &child_iter,
00605                             TREE_COL_NAME, &child_name,
00606                             TREE_COL_TYPE, &type,
00607                             -1);
00608         switch (type) {
00609         case MENU_TYPE_GROUP:
00610             menustore_remove_group(child_name);
00611             break;
00612         case MENU_TYPE_ITEM:
00613             menustore_remove_item(child_name, name);
00614             break;
00615         }
00616         g_free(child_name);
00617     }
00618 
00619     // remove group itself
00620     gtk_tree_store_remove(GTK_TREE_STORE(menu_store), menu_iter);
00621     gtk_tree_iter_free(menu_iter);
00622 
00623     g_popup_has_changed = TRUE;
00624     g_toolbar_has_changed = TRUE;
00625     return TRUE;
00626 }
00627 
00628 
00629 gboolean menustore_remove_item(const char *iname, const char* pname)
00630 {
00631     TRACE("%s() iname=%s  pname=%s\n", __func__, iname, pname);
00632 
00633     // check parent name
00634     if (pname == 0 || strlen(pname) == 0) {
00635         WARNPRINTF("no pname (iname=%s)", iname);
00636         return FALSE;
00637     }
00638 
00639     // find parent
00640     GtkTreeIter *parent_iter = get_iter(pname);
00641     if (!parent_iter) {
00642         WARNPRINTF("parent does not exist! (iname=%s, pname=%s)", iname, pname);
00643         return FALSE;
00644     }
00645 
00646     // check if child exists
00647     GtkTreeIter *child_iter = get_child_by_iter(iname, parent_iter);
00648     if (!child_iter) {
00649         LOGPRINTF("'%s.%s' not found", pname, iname);
00650         return FALSE;
00651     }
00652 
00653     if (!check_type(child_iter, iname, MENU_TYPE_ITEM)) return FALSE;
00654 
00655     // remove item
00656     gtk_tree_store_remove(GTK_TREE_STORE(menu_store), child_iter);
00657     gtk_tree_iter_free(child_iter);
00658 
00659     g_popup_has_changed = TRUE;
00660     g_toolbar_has_changed = TRUE;
00661     return TRUE;
00662 }
00663 
00664 
00665 static void free_menu_entry(MenuEntry* entry)
00666 {
00667     g_free(entry->name);
00668     g_free(entry->service);
00669     g_free(entry);
00670 }
00671 
00672 
00673 gboolean menustore_remove_menu(const char *name)
00674 {
00675     TRACE("%s() name=%s\n", __func__, name);
00676 
00677     GList *iter = g_list_first(menu_list);
00678     while (iter != NULL)
00679     {
00680         MenuEntry *entry = (MenuEntry *) iter->data;
00681         if (strcmp(entry->name, name) == 0) break;
00682         iter = g_list_next(iter);
00683     }
00684 
00685     if (iter == NULL) {
00686         WARNPRINTF("menu '%s' not found", name);
00687         return FALSE;
00688     }
00689 
00690     free_menu_entry(iter->data);
00691     menu_list = g_list_delete_link(menu_list, iter);
00692 
00693     if (current_menu && strcmp(current_menu, name) == 0)
00694     {
00695         menustore_set_current_menu(NULL);
00696     }
00697 
00698     g_popup_has_changed = TRUE;
00699     g_toolbar_has_changed = TRUE;
00700     return TRUE;
00701 }
00702 
00703 
00704 static GdkPixbuf *get_pixbuf(enum menu_state state, GtkTreeIter *menu_iter)
00705 {
00706     GdkPixbuf *img = NULL;
00707     switch (state)
00708     {
00709         case MENU_STATE_NORMAL:
00710         case MENU_STATE_SELECTED:
00711         case MENU_STATE_DISABLED:
00712             gtk_tree_model_get(GTK_TREE_MODEL(menu_store), menu_iter,
00713                                TREE_COL_IMAGE_NORMAL, &img,
00714                                -1);
00715             break;
00716 
00717         case MENU_STATE_ALTERNATE:
00718             gtk_tree_model_get(GTK_TREE_MODEL(menu_store), menu_iter,
00719                                TREE_COL_IMAGE_ALTERNATE, &img,
00720                                -1);
00721             break;
00722     }
00723     if (img == NULL)
00724     {
00725         if (state == MENU_STATE_SELECTED)
00726         {
00727             img = pixlist_icon_state("selected", "normal");
00728         } else {
00729             img = pixlist_icon_state("blank", "normal");
00730         }
00731         if (img) g_object_ref(img);
00732     }
00733     return img;
00734 }
00735 
00736 
00737 static gboolean update_state(GtkTreeIter* iter, const char* iname, const char* pname, const char* stateStr)
00738 {
00739     enum menu_state state = string2state(stateStr);
00740     if ((gint) state == -1) return FALSE;
00741 
00742     // update state in menu store
00743     gtk_tree_store_set(GTK_TREE_STORE(menu_store), iter,
00744                      TREE_COL_STATE, state,
00745                      -1);
00746 
00747     g_popup_has_changed = TRUE;
00748     g_toolbar_has_changed = TRUE;
00749     return TRUE;
00750 }
00751 
00752 
00753 gboolean menustore_set_group_state(const char *name, const char *stateStr)
00754 {
00755     TRACE("%s() name=%s  state=%s\n", __func__, name, stateStr);
00756 
00757     GtkTreeIter *iter = get_iter(name);
00758     if (iter == NULL)
00759     {
00760         LOGPRINTF("'%s' not found", name);
00761         return FALSE;
00762     }
00763 
00764     if (!check_type(iter, name, MENU_TYPE_GROUP)) return FALSE;
00765 
00766     return update_state(iter, name, NULL, stateStr);
00767 }
00768 
00769 
00770 gboolean menustore_set_item_state(const char *iname, const char *pname, const char *stateStr)
00771 {
00772     TRACE("%s() iname=%s  pname=%s  state=%s\n", __func__, iname, pname, stateStr);
00773 
00774     if ( (pname != NULL) && strcmp(pname, "general")==0 )
00775     {
00776         pname = "system_bottom";
00777     }
00778     
00779     // check item
00780     GtkTreeIter *iter = get_child_by_name(iname, pname);
00781     if (iter == NULL)
00782     {
00783         LOGPRINTF("'%s.%s' not found", pname, iname);
00784         return FALSE;
00785     }
00786 
00787     return update_state(iter, iname, pname, stateStr);
00788 }
00789 
00790 
00791 static void update_label(GtkTreeIter* iter, const char* iname, const char* pname, const char* label)
00792 {
00793     // update state in menu store
00794     gtk_tree_store_set(GTK_TREE_STORE(menu_store), iter,
00795                        TREE_COL_TEXT, label,
00796                        -1);
00797     g_popup_has_changed = TRUE;
00798 }
00799 
00800 
00801 gboolean menustore_set_group_label(const char *name, const char *label)
00802 {
00803     TRACE("%s() name=%s  label=%s\n", __func__, name, label);
00804 
00805     // check name
00806     GtkTreeIter *iter = get_iter(name);
00807     if (iter == NULL)
00808     {
00809         LOGPRINTF("'%s' not found", name);
00810         return FALSE;
00811     }
00812 
00813     // check type
00814     if (!check_type(iter, name, MENU_TYPE_GROUP)) return FALSE;
00815 
00816     update_label(iter, name, NULL, label);
00817     return TRUE;
00818 }
00819 
00820 
00821 gboolean menustore_set_item_label(const char *iname, const char *pname, const char *label)
00822 {
00823     TRACE("%s() iname=%s  pname=%s  label=%s\n", __func__, iname, pname, label);
00824 
00825     // check name
00826     GtkTreeIter *iter = get_child_by_name(iname, pname);
00827     if (iter == NULL)
00828     {
00829         LOGPRINTF("'%s.%s' not found", pname, iname);
00830         return FALSE;
00831     }
00832 
00833     update_label(iter, iname, pname, label);
00834     return TRUE;
00835 }
00836 
00837 
00838 gboolean menustore_popup_has_changed()
00839 {
00840     return g_popup_has_changed;
00841 }
00842 
00843 
00844 void menustore_clear_popup_changed()
00845 {
00846     g_popup_has_changed = FALSE;
00847 }
00848 
00849 
00850 #if MACHINE_IS_DR1000S || MACHINE_IS_DR1000SW
00851 gboolean menustore_toolbar_has_changed()
00852 {
00853     return g_toolbar_has_changed;
00854 }
00855 
00856 
00857 void menustore_clear_toolbar_changed()
00858 {
00859     g_toolbar_has_changed = FALSE;
00860 }
00861 
00862 
00863 static MenuToolItems* find_tools_for_menu(const char* menu)
00864 {
00865     if (menu == NULL) return NULL;
00866 
00867     int num = sizeof(hardcoded_tools) / sizeof(hardcoded_tools[0]);
00868     int i;
00869     for (i=0; i<num; i++) {
00870         const char* hardcoded = hardcoded_tools[i].menu;
00871         // NOTE: menu name must start with hardcoded name. (for menu_notepad_2831)
00872         if (strncmp(hardcoded, menu, strlen(hardcoded)) == 0) {
00873             return &hardcoded_tools[i];
00874         }
00875     }
00876     return NULL;
00877 }
00878 
00879 
00880 int menustore_get_tool_limit()
00881 {
00882     // HACK to work around 4/16 slots. If slot[4] exists, we have a long one
00883     MenuToolItems* tools = find_tools_for_menu(current_menu);
00884     if (tools == NULL) return 0;
00885 
00886     ToolItem* item = &tools->items[MENUSTORE_SMALL_TOOLS];
00887     if (item->pname == NULL) return MENUSTORE_SMALL_TOOLS;
00888     return MENUSTORE_NUM_TOOLS;
00889 }
00890 
00891 
00892 GdkPixbuf* menustore_get_tool_icon(int index)
00893 {
00894     g_assert(index < MENUSTORE_NUM_TOOLS);
00895 
00896     MenuToolItems* tools = find_tools_for_menu(current_menu);
00897     if (tools == NULL) goto blank;
00898 
00899     ToolItem* item = &tools->items[index];
00900     if (item->pname == NULL || item->iname == NULL) goto blank;
00901     GtkTreeIter *iter = get_child_by_name(item->iname, item->pname);
00902     if (iter == NULL) {
00903         ERRORPRINTF("'%s.%s' not found", item->pname, item->iname);
00904         goto blank;
00905     }
00906 
00907     enum menu_state state;
00908     gchar *icon_name = NULL;
00909     gtk_tree_model_get(GTK_TREE_MODEL(menu_store), iter,
00910                        TREE_COL_STATE, &state,
00911                        TREE_COL_ICON, &icon_name,
00912                        -1);
00913     GdkPixbuf* img = pixlist_toolbar_icon(icon_name, state2str[state]);
00914     g_free(icon_name);
00915     return img;
00916 blank:
00917     return pixlist_icon_state("toolbar_blank", "normal");
00918 }
00919 
00920 
00921 void menustore_activate_toolitem(int index, tool_func_t func)
00922 {
00923     g_assert(index < MENUSTORE_NUM_TOOLS);
00924 
00925     // check current menu
00926     MenuToolItems* tools = find_tools_for_menu(current_menu);
00927     if (tools == NULL) {
00928         WARNPRINTF("no tool for menu");
00929         return;
00930     }
00931 
00932     // find item
00933     ToolItem* item = &tools->items[index];
00934     if (item->pname == NULL || item->iname == NULL) return;
00935 
00936     menustore_activate_item(item->pname, item->iname, func);
00937 }
00938 #endif
00939 
00940 
00941 gboolean menustore_activate_item_iter(gpointer user_data, tool_func_t func)
00942 {
00943     GtkTreeIter* iter = (GtkTreeIter*) user_data;
00944 
00945     gchar *iname;
00946     enum menu_type type = MENU_TYPE_ITEM;
00947     enum menu_state state = MENU_STATE_NORMAL;
00948     gtk_tree_model_get(GTK_TREE_MODEL (menu_store), iter,
00949                        TREE_COL_TYPE, &type,
00950                        TREE_COL_NAME, &iname,
00951                        TREE_COL_STATE, &state,
00952                        -1);
00953     
00954     if (type != MENU_TYPE_ITEM) {
00955         goto error;
00956     }
00957     if (state == MENU_STATE_DISABLED)
00958     {
00959         WARNPRINTF("cannot activate disabled item");
00960         goto error;
00961     }
00962 
00963     GtkTreeIter parent_iter;
00964     gtk_tree_model_iter_parent(GTK_TREE_MODEL (menu_store), 
00965                                &parent_iter,
00966                                iter);
00967     gchar *pname;
00968     gtk_tree_model_get(GTK_TREE_MODEL (menu_store), &parent_iter,
00969                        TREE_COL_NAME, &pname,
00970                        -1);
00971 
00972     if ((strcmp(pname, "system_top") == 0) || (strcmp(pname, "system_bottom") == 0))
00973     {
00974 #if MACHINE_IS_DR1000S || MACHINE_IS_DR1000SW
00975         // special case for Taskbar for "Home" and "Back to category"
00976         if ( (strcmp(iname, "desktop") == 0) || (strcmp(iname, "back_to_library") == 0) )
00977         {
00978             taskbar_home_cb();
00979         }
00980 #endif
00981         if (strcmp(iname, "back_to_library") == 0)
00982         {
00983             func(iname, "tasks", current_menu, state2str[state], DBUS_SERVICE_SYSTEM_CONTROL);
00984         }
00985         else
00986         {
00987             func(iname, "general", current_menu, state2str[state], DBUS_SERVICE_SYSTEM_CONTROL);
00988         }
00989     }
00990     else
00991     {
00992         func(iname, pname, current_menu, state2str[state], menustore_get_current_service());
00993     }
00994     g_free(iname);
00995     g_free(pname);
00996     return TRUE;
00997 error:
00998     g_free(iname);
00999     return FALSE;
01000 }
01001 
01002 
01003 void menustore_activate_item(const char* pname, const char* iname, tool_func_t func)
01004 {
01005     LOGPRINTF("entry [%s.%s]", pname, iname);
01006 
01007     GtkTreeIter *iter = get_child_by_name(iname, pname);
01008     if (iter == NULL) {
01009         ERRORPRINTF("'%s.%s' not found", pname, iname);
01010         return;
01011     }
01012     menustore_activate_item_iter(iter, func);
01013 }
01014 
01015 
01016 static gboolean fill_add_group(const char* group,
01017                                add_item_func item_cb,
01018                                add_item_func submenu_cb,
01019                                separator_func separator_cb,
01020                                gpointer user_data)
01021 {
01022     GtkTreeIter *menu_iter = get_iter(group);
01023     if (menu_iter == NULL)
01024     {
01025         WARNPRINTF("entry: name [%s] not found", group);
01026         return FALSE;
01027     }
01028 
01029     // check if group has children
01030     gint num_items = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(menu_store), menu_iter);
01031     if (num_items == 0)
01032     {
01033         WARNPRINTF("given group `%s` has no children", group);
01034         return FALSE;
01035     }
01036     
01037     gboolean rc = FALSE;
01038 
01039     GtkTreeIter child_iter;
01040     gboolean result = gtk_tree_model_iter_children(GTK_TREE_MODEL(menu_store), &child_iter, menu_iter);
01041     while (result == TRUE)
01042     {
01043         // get info from child
01044         gchar *name = NULL;
01045         gchar *text = NULL;
01046         enum menu_type  type = MENU_TYPE_ITEM;
01047         enum menu_state state = MENU_STATE_NORMAL;
01048         gtk_tree_model_get(GTK_TREE_MODEL(menu_store), &child_iter,
01049                            TREE_COL_NAME, &name,
01050                            TREE_COL_TEXT, &text,
01051                            TREE_COL_STATE, &state,
01052                            TREE_COL_TYPE, &type,
01053                            -1);
01054 
01055         if (state != MENU_STATE_DISABLED && strcmp("", text) == 0) {
01056             LOGPRINTF("empty label for %s.%s", group, name);
01057         }
01058 
01059         GdkPixbuf *img = get_pixbuf(state, &child_iter);
01060         GtkTreeIter *iter_copy = gtk_tree_iter_copy(&child_iter);
01061 
01062         int count = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(menu_store), &child_iter);
01063         if (type == MENU_TYPE_GROUP && count != 0) {
01064             gpointer user_data2 = submenu_cb(name, text, state, img, iter_copy, user_data);
01065             separator_cb(user_data2);
01066             GtkTreeIter child_child;
01067             gboolean is_next = gtk_tree_model_iter_children(GTK_TREE_MODEL(menu_store), &child_child, &child_iter);
01068             while (is_next)
01069             {
01070                 gchar *group_name = NULL;
01071                 gtk_tree_model_get(GTK_TREE_MODEL(menu_store), &child_child,
01072                                    TREE_COL_NAME, &group_name,
01073                                    -1);
01074 
01075                 // add group to submenu
01076                 gboolean is_added = fill_add_group(group_name, item_cb, submenu_cb, separator_cb, user_data2);
01077                 g_free(group_name);
01078 
01079                 is_next = gtk_tree_model_iter_next(GTK_TREE_MODEL(menu_store), &child_child);
01080                 if (is_added && is_next)
01081                 {
01082                     // group follows, add separator line
01083                     separator_cb(user_data2);
01084                 }
01085             }
01086         } else {
01087             item_cb(name, text, state, img, iter_copy, user_data);
01088         }
01089 
01090         pixbuf_unref(img);
01091         g_free(name);
01092         g_free(text);
01093 
01094         if (state != MENU_STATE_DISABLED) rc = TRUE;
01095 
01096         result = gtk_tree_model_iter_next(GTK_TREE_MODEL(menu_store), &child_iter);
01097     }
01098     return rc;
01099 }
01100 
01101 
01102 static const GList* get_menu_groups()
01103 {
01104     MenuEntry *entry = find_menu(current_menu);
01105     if (entry) return entry->groups;
01106     return NULL;
01107 }
01108 
01109 
01110 void menustore_fill_menu(add_item_func item_cb,
01111                          add_item_func submenu_cb,
01112                          separator_func separator_cb,
01113                          gpointer user_data)
01114 {
01115     fill_add_group("system_top", item_cb, submenu_cb, separator_cb, user_data);
01116     separator_cb(user_data);
01117 
01118     gboolean is_added = FALSE;
01119     const GList *groups = get_menu_groups();
01120     while (groups) {
01121         is_added = fill_add_group((const char*)groups->data, item_cb, submenu_cb, separator_cb, user_data);
01122 
01123         if (is_added) separator_cb(user_data);
01124 
01125         groups = g_list_next(groups);
01126     }
01127 
01128     fill_add_group("system_bottom", item_cb, submenu_cb, separator_cb, user_data);
01129 }
01130 
01131 
01132 #if (TESTING_ON)
01133 static void testing_extra_prints()
01134 {
01135     printf("Current menu = '%s'  service='%s'\n",
01136             current_menu, menustore_get_current_service());
01137     printf("---- %s() MENULIST:\n", __func__);
01138     GList *iter = g_list_first(menu_list);
01139     while (iter != NULL)
01140     {
01141         MenuEntry *entry = (MenuEntry *) iter->data;
01142         printf("%s  service=%s\n", entry->name, entry->service);
01143         GList *iter2 = g_list_first(entry->groups);
01144         while (iter2) {
01145             printf("  %s\n", (const char*)iter2->data);
01146             iter2 = g_list_next(iter2);
01147         }
01148         iter = g_list_next(iter);
01149     }
01150 
01151     printf("\n---- %s() MENU_STORE:\n", __func__);
01152     debug_count = 0;
01153     gtk_tree_model_foreach(GTK_TREE_MODEL(menu_store), printfunc, NULL);
01154 
01155 #if MACHINE_IS_DR1000S || MACHINE_IS_DR1000SW
01156     printf("\n---- %s() HARDCODED TOOLITEMS:\n", __func__);
01157     int num = sizeof(hardcoded_tools) / sizeof(hardcoded_tools[0]);
01158     int i;
01159     for (i=0; i<num; i++) {
01160         MenuToolItems* tools = &hardcoded_tools[i];
01161 
01162         printf("  %s\n", tools->menu);
01163         int i;
01164         for (i=0; i<MENUSTORE_NUM_TOOLS; i++) {
01165             if (tools->items[i].pname) {
01166                 printf("    [%2d] %s.%s\n", i, tools->items[i].pname, tools->items[i].iname);
01167             }
01168         }
01169     }
01170 #endif
01171 }
01172 
01173 static char buffer[1000];
01174 static char* cp = NULL;
01175 
01176 gpointer print_item(const char* name,
01177                     const char* text,
01178                     enum menu_state state,
01179                     GdkPixbuf* img,
01180                     gpointer menu_data,
01181                     gpointer user_data)
01182 {
01183     strcpy(cp, name);
01184     cp += strlen(name);
01185     *cp++ = '\n';
01186     return NULL;
01187 }
01188 
01189 
01190 void print_separator(gpointer user_data) {} // ignore
01191 
01192 
01193 static gboolean find_route(const char* pname, const char* iname, const char* group, char** route, gboolean top_lvl)
01194 {
01195     char* orig = *route;
01196     char *rp = *route;
01197     GtkTreeIter *group_iter = get_iter(group);
01198     g_assert(group_iter);
01199 
01200     GtkTreeIter child_iter;
01201     gboolean result = gtk_tree_model_iter_children(GTK_TREE_MODEL(menu_store), &child_iter, group_iter);
01202     gboolean found = FALSE;
01203     while (result == TRUE)
01204     {
01205         // get child info
01206         gchar *name = NULL;
01207         gchar *text = NULL;
01208         enum menu_type type = MENU_TYPE_ITEM;
01209         enum menu_state state = MENU_STATE_NORMAL;
01210         gtk_tree_model_get(GTK_TREE_MODEL(menu_store), &child_iter,
01211                            TREE_COL_NAME, &name,
01212                            TREE_COL_TEXT, &text,
01213                            TREE_COL_STATE, &state,
01214                            TREE_COL_TYPE, &type,
01215                            -1);
01216         gboolean visible = TRUE;
01217         if (strcmp(name, iname) == 0 && strcmp(group, pname) == 0) found = TRUE;
01218         if (state == MENU_STATE_DISABLED || strcmp(text, "") == 0) visible = FALSE;
01219         g_free(text);
01220         if (found) {
01221             if (!visible) {
01222                 // not visible
01223                 *orig++ = 'X';
01224                 *orig = 0;
01225             } else {
01226                 // match
01227                 *rp++ = 'E';
01228                 *rp = 0;
01229 
01230             }
01231             g_free(name);
01232             return TRUE; 
01233         } else {
01234             if (visible) {
01235                 // TODO groups can have '' name, does this occur?
01236                 if (type == MENU_TYPE_GROUP) {
01237                     char* before = rp;
01238                     if (top_lvl) {
01239                         *rp++ = 'E';
01240                         *rp++ = 'D';
01241                         *route = rp;
01242                     }
01243                     // search recursively
01244                     gboolean found2 = find_route(pname, iname, name, route, FALSE);
01245                     if (found2) {
01246                         g_free(name);
01247                         return TRUE;
01248                     } else {
01249                         if (top_lvl) {  // backtrack submenu
01250                             rp = before;
01251                             *rp++ = 'D';
01252                             *route = rp;
01253                         }
01254                     }
01255                 } else {
01256                     *rp++ = 'D';
01257                     *route = rp;
01258                 }
01259             } else {
01260                 // skipping
01261             }
01262         }
01263 
01264         g_free(name);
01265         result = gtk_tree_model_iter_next(GTK_TREE_MODEL(menu_store), &child_iter);
01266     }
01267     return FALSE;
01268 }
01269 
01270 
01271 static const char* testing_route_to_item(const char* pname, const char* iname)
01272 {
01273     char route[128];
01274     memset(route, 0, sizeof(route));
01275     char *cp = &route[0];
01276 
01277     gboolean found = find_route(pname, iname, "system_top", &cp, TRUE);
01278     if (found) goto out_found;
01279 
01280     const GList *groups = get_menu_groups();
01281     while (groups) {
01282         found = find_route(pname, iname, (const char*)groups->data, &cp, TRUE);
01283         if (found) goto out_found;
01284         groups = g_list_next(groups);
01285     }
01286 
01287     found = find_route(pname, iname, "system_bottom", &cp, TRUE);
01288 out_found:
01289     if (found && route[strlen(route)-1] == 'X') found = FALSE;
01290     if (!found) {
01291         route[0] = 'X'; 
01292         route[1] = 0;
01293     }
01294     // add spaces to make parsing easier
01295     static char result[256];
01296     char *ip = &route[0];
01297     char *op = &result[0];
01298     unsigned int i = 0;
01299     for (i=0; i<strlen(route); i++) {
01300         *op++ = *ip++; 
01301         *op++ = ' ';
01302     }
01303     *op = 0;
01304     return result;
01305 }
01306 
01307 
01308 const char* testing_menustore_print()
01309 {
01310     testing_extra_prints();
01311 
01312     cp = buffer;
01313     menustore_fill_menu(print_item, print_item, print_separator, NULL);
01314     *cp = 0;
01315     return buffer;
01316 }
01317 
01318 
01319 const char* testing_menustore_get_route(const char* pname, const char* iname)
01320 {
01321     return testing_route_to_item(pname, iname);
01322 }
01323 
01324 #endif // TESTING_ON
01325 
Generated by  doxygen 1.6.2-20100208