Index: apps/bookmark.c
===================================================================
--- apps/bookmark.c (revision 14960)
+++ apps/bookmark.c (working copy)
@@ -596,6 +596,11 @@
}
}
+
+MENUITEM_STRINGLIST(bookmark_menu_items, ID2P(LANG_BOOKMARK_CONTEXT_MENU),
+ NULL, ID2P(LANG_BOOKMARK_CONTEXT_RESUME),
+ ID2P(LANG_BOOKMARK_CONTEXT_DELETE));
+
/* ----------------------------------------------------------------------- */
/* This displays a the bookmarks in a file and allows the user to */
/* select one to play. */
@@ -683,14 +688,11 @@
if (action == ACTION_STD_CONTEXT)
{
- MENUITEM_STRINGLIST(menu_items, ID2P(LANG_BOOKMARK_CONTEXT_MENU),
- NULL, ID2P(LANG_BOOKMARK_CONTEXT_RESUME),
- ID2P(LANG_BOOKMARK_CONTEXT_DELETE));
static const int menu_actions[] =
{
ACTION_STD_OK, ACTION_BMS_DELETE
};
- int selection = do_menu(&menu_items, NULL);
+ int selection = do_menu(&bookmark_menu_items, NULL);
refresh = true;
Index: apps/lang/english.lang
===================================================================
--- apps/lang/english.lang (revision 14960)
+++ apps/lang/english.lang (working copy)
@@ -11341,3 +11341,17 @@
*: "Central Eurpean"
+
+ id: LANG_MENU_AUTO_REORDERING
+ desc: somewhere in the menu (system?)
+ user:
+
+ *: "Auto Menu Reordering"
+
+
+ *: "Auto Menu Reordering"
+
+
+ *: "Auto Menu Reordering"
+
+
Index: apps/Makefile
===================================================================
--- apps/Makefile (revision 14960)
+++ apps/Makefile (working copy)
@@ -56,7 +56,7 @@
CFLAGS = $(INCLUDES) $(GCCOPTS) $(TARGET) $(BUILDDATE) $(DEFINES) -DTARGET_ID=$(TARGET_ID) \
-DAPPSVERSION=\"$(VERSION)\" $(EXTRA_DEFINES) -DMEM=${MEMORYSIZE} \
- -DTARGET_NAME=\"$(ARCHOS)\"
+ -DTARGET_NAME=\"$(ARCHOS)\" -DGATHER_MENU_CALL_STATS -save-temps
OBJS2 := $(OBJDIR)/lang.o $(patsubst %.c, $(OBJDIR)/%.o, $(SRC))
OBJS = $(patsubst %.S, $(OBJDIR)/%.o, $(OBJS2))
Index: apps/menu.c
===================================================================
--- apps/menu.c (revision 14960)
+++ apps/menu.c (working copy)
@@ -161,6 +161,63 @@
}
#endif
+/* The array (and the var for its length) is defined and initialized
+ in an automatically generated file 'menu_call_stats_list.c' and
+ contains pointers to the call statistics for all menu items
+ in the core Rockbox */
+extern struct s_call_stats * const menu_call_stats_list[];
+extern const int menu_call_stats_list_length;
+void get_menu_call_stats_list(struct s_call_stats * const **list, int *list_length)
+{
+ (*list) = menu_call_stats_list;
+ (*list_length) = menu_call_stats_list_length;
+}
+
+static void reorder_menu_items_by_freq(const struct menu_item_ex *menu)
+{
+ int type;
+ struct s_call_stats *stats;
+ const struct menu_item_ex **items;
+ const struct menu_item_ex *tmp;
+ int item_count;
+ int i, j, max, max_index;
+
+ type = menu->flags & MENU_TYPE_MASK;
+
+ DEBUGF("Reordering menu with id='%s', type=%d\n",
+ (menu->call_stats!=0?menu->call_stats->item_id:"---"),
+ type);
+ /* Is this a reordable menu? */
+ if (type != MT_MENU)
+ {
+ return;
+ }
+
+ items = menu->submenus;
+ item_count = MENU_GET_COUNT(menu->flags);
+ for (i=0; icall_stats;
+ if ((stats != NULL) && (stats->call_count > max))
+ {
+ max = stats->call_count;
+ max_index = j;
+ }
+ }
+ if (max_index >= 0)
+ {
+ tmp = items[i];
+ items[i] = items[max_index];
+ items[max_index] = tmp;
+ }
+ }
+}
+
+
static void init_menu_lists(const struct menu_item_ex *menu,
struct gui_synclist *lists, int selected, bool callback)
{
@@ -170,6 +227,9 @@
int icon;
current_subitems_count = 0;
+ /* Reorder menu items (most frequently selected go to the top) */
+ reorder_menu_items_by_freq(menu);
+
if (type == MT_RETURN_ID)
get_menu_callback(menu, &menu_callback);
@@ -418,6 +478,13 @@
#endif
selected = get_menu_selection(gui_synclist_get_sel_pos(&lists), menu);
temp = menu->submenus[selected];
+ if (global_settings.menu_auto_reorder && (temp->call_stats != NULL))
+ {
+ temp->call_stats->call_count++;
+ DEBUGF("Selected item: id='%s', call_cnt=%d, type=%d\n",
+ temp->call_stats->item_id, temp->call_stats->call_count,
+ temp->flags&MENU_TYPE_MASK);
+ }
redraw_lists = true;
if (in_stringlist)
type = (menu->flags&MENU_TYPE_MASK);
@@ -468,11 +535,9 @@
case MT_SETTING:
case MT_SETTING_W_TEXT:
{
- if (do_setting_from_menu(temp))
- {
- init_menu_lists(menu, &lists, selected, true);
- redraw_lists = false; /* above does the redraw */
- }
+ do_setting_from_menu(temp);
+ init_menu_lists(menu, &lists, selected, true);
+ redraw_lists = false; /* above does the redraw */
talk_item = true;
break;
}
Index: apps/menu.h
===================================================================
--- apps/menu.h (revision 14960)
+++ apps/menu.h (working copy)
@@ -25,6 +25,11 @@
#include "icons.h"
#include "root_menu.h" /* needed for MENU_* return codes */
+/* Define the following if the statistics about the selected menu
+ items should be gathered */
+/* GATHER_MENU_CALL_STATS must be defined in the Makefile -- for core only.
+ For plugins and codecs, it should NOT be defined */
+/*#define GATHER_MENU_CALL_STATS*/
enum menu_item_type {
MT_MENU = 0,
@@ -94,6 +99,12 @@
int icon_id;
} *menu_get_name_and_icon;
};
+ /* The following pointer will be initialized with NULL if
+ GATHER_MENU_CALL_STATS is undefined */
+ struct s_call_stats {
+ char *item_id; /* id of the item, should be unique across all menues */
+ int call_count; /* How many times this item was selected */
+ } * const call_stats;
};
typedef int (*menu_callback_type)(int action,
@@ -124,23 +135,49 @@
- callback: The callback function to call for this menu item.
*/
+#ifdef GATHER_MENU_CALL_STATS
+#define STATS_STRUCT(name) \
+ struct s_call_stats item_call_stats_##name = {#name, 0}
+#define ITEMS_STATS(name) \
+ &item_call_stats_##name
+#else
+#define STATS_STRUCT(name)
+#define ITEMS_STATS(name) \
+ NULL
+#endif /* GATHER_MENU_CALL_STATS */
+
+
+/* Function for getting information about selecting of menu items */
+void get_menu_call_stats_list(struct s_call_stats * const **list, int *list_length);
+
+/*
+ * Important: all the MENUITEM_xxx macros should only be used at the top
+ * level, i.e. NOT within a function body, if the symbol GATHER_MENU_CALL_STATS
+ * is defined. If the symbol is not defined, the macros can also be used within
+ * a function body. But always using it at the top level would do no harm.
+ */
+
/* Use this to put a setting into a menu.
The setting must appear in settings_list.c.
If the setting is not configured properly, the menu will display "Not Done yet!"
When the user selects this item the setting select screen will load,
when that screen exits the user wll be back in the menu */
#define MENUITEM_SETTING(name,var,callback) \
+ STATS_STRUCT(name); \
static const struct menu_item_ex name = \
- {MT_SETTING, {.variable = (void*)var},{callback}};
+ {MT_SETTING, {.variable = (void*)var},{callback}, \
+ ITEMS_STATS(name)}
/* Use this for settings which have a differnt title in their
setting screen than in the menu (e.g scroll options */
#define MENUITEM_SETTING_W_TEXT(name, var, str, callback ) \
static const struct menu_callback_with_desc name##__ = \
{callback,str, Icon_NOICON}; \
+ STATS_STRUCT(name); \
static const struct menu_item_ex name = \
{MT_SETTING_W_TEXT|MENU_HAS_DESC, {.variable = (void*)var }, \
- {.callback_and_desc = & name##__}};
+ {.callback_and_desc = & name##__}, \
+ ITEMS_STATS(name)}
/* Use this To create a list of Strings (or ID2P()'s )
When the user enters this list and selects one, the menu will exit
@@ -150,27 +187,33 @@
static const char *name##_[] = {__VA_ARGS__}; \
static const struct menu_callback_with_desc name##__ = \
{callback,str, Icon_NOICON}; \
+ STATS_STRUCT(name); \
static const struct menu_item_ex name = \
{MT_RETURN_ID|MENU_HAS_DESC| \
MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \
- { .strings = name##_},{.callback_and_desc = & name##__}};
+ { .strings = name##_},{.callback_and_desc = & name##__}, \
+ ITEMS_STATS(name)}
/* causes do_menu() to return a value associated with the item */
#define MENUITEM_RETURNVALUE(name, str, val, cb, icon) \
static const struct menu_callback_with_desc name##_ = {cb,str,icon}; \
+ STATS_STRUCT(name); \
static const struct menu_item_ex name = \
{ MT_RETURN_VALUE|MENU_HAS_DESC, { .value = val}, \
- {.callback_and_desc = & name##_}};
+ {.callback_and_desc = & name##_}, \
+ ITEMS_STATS(name)}
/* same as above, except the item name is dynamic */
#define MENUITEM_RETURNVALUE_DYNTEXT(name, val, cb, text_callback, \
text_cb_data, icon) \
static const struct menu_get_name_and_icon name##_ \
= {cb,text_callback,text_cb_data,icon}; \
+ STATS_STRUCT(name); \
static const struct menu_item_ex name = \
{ MT_RETURN_VALUE|MENU_DYNAMIC_DESC, { .value = val}, \
- {.menu_get_name_and_icon = & name##_}};
+ {.menu_get_name_and_icon = & name##_}, \
+ ITEMS_STATS(name)}
/* Use this to put a function call into the menu.
When the user selects this item the function will be run,
@@ -182,9 +225,11 @@
static const struct menu_callback_with_desc name##_ = {callback,str,icon}; \
static const struct menu_func name##__ = {{(void*)func}, param}; \
/* should be const, but recording_settings wont let us do that */ \
+ STATS_STRUCT(name); \
const struct menu_item_ex name = \
{ MT_FUNCTION_CALL|MENU_HAS_DESC|flags, \
- { .function = & name##__}, {.callback_and_desc = & name##_}};
+ { .function = & name##__}, {.callback_and_desc = & name##_}, \
+ ITEMS_STATS(name)}
/* As above, except the text is dynamic */
#define MENUITEM_FUNCTION_DYNTEXT(name, flags, func, param, \
@@ -192,9 +237,11 @@
static const struct menu_get_name_and_icon name##_ \
= {callback,text_callback,text_cb_data,icon}; \
static const struct menu_func name##__ = {{(void*)func}, param}; \
+ STATS_STRUCT(name); \
static const struct menu_item_ex name = \
{ MT_FUNCTION_CALL|MENU_DYNAMIC_DESC|flags, \
- { .function = & name##__}, {.menu_get_name_and_icon = & name##_}};
+ { .function = & name##__}, {.menu_get_name_and_icon = & name##_}, \
+ ITEMS_STATS(name)}
/* Use this to actually create a menu. the ... argument is a list of pointers
to any of the above macro'd variables.
@@ -202,10 +249,12 @@
#define MAKE_MENU( name, str, callback, icon, ... ) \
static const struct menu_item_ex *name##_[] = {__VA_ARGS__}; \
static const struct menu_callback_with_desc name##__ = {callback,str,icon};\
+ STATS_STRUCT(name); \
const struct menu_item_ex name = \
{MT_MENU|MENU_HAS_DESC| \
MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \
- { (void*)name##_},{.callback_and_desc = & name##__}};
+ { .submenus = name##_},{.callback_and_desc = & name##__}, \
+ ITEMS_STATS(name)}
#endif /* End __MENU_H__ */
Index: apps/onplay.c
===================================================================
--- apps/onplay.c (revision 14960)
+++ apps/onplay.c (working copy)
@@ -81,12 +81,14 @@
#define MAKE_ONPLAYMENU( name, str, callback, icon, ... ) \
static const struct menu_item_ex *name##_[] = {__VA_ARGS__}; \
static const struct menu_callback_with_desc name##__ = {callback,str,icon};\
+ STATS_STRUCT(name); \
static const struct menu_item_ex name = \
{MT_MENU|MENU_HAS_DESC|MENU_EXITAFTERTHISMENU| \
MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \
- { (void*)name##_},{.callback_and_desc = & name##__}};
-
+ { (void*)name##_},{.callback_and_desc = & name##__}, \
+ ITEMS_STATS(name)};
+
/* ----------------------------------------------------------------------- */
/* Displays the bookmark menu options for the user to decide. This is an */
/* interface function. */
Index: apps/playlist_viewer.c
===================================================================
--- apps/playlist_viewer.c (revision 14960)
+++ apps/playlist_viewer.c (working copy)
@@ -431,17 +431,17 @@
/* Menu of playlist commands. Invoked via ON+PLAY on main viewer screen.
Returns -1 if USB attached, 0 if no playlist change, and 1 if playlist
changed. */
+MENUITEM_STRINGLIST(playlist_viewer_menu_items, ID2P(LANG_PLAYLIST), NULL,
+ ID2P(LANG_REMOVE), ID2P(LANG_MOVE),
+ ID2P(LANG_CATALOG_ADD_TO), ID2P(LANG_CATALOG_ADD_TO_NEW));
static int onplay_menu(int index)
{
int result, ret = 0;
struct playlist_entry * current_track=
playlist_buffer_get_track(&viewer.buffer, index);
- MENUITEM_STRINGLIST(menu_items, ID2P(LANG_PLAYLIST), NULL,
- ID2P(LANG_REMOVE), ID2P(LANG_MOVE),
- ID2P(LANG_CATALOG_ADD_TO), ID2P(LANG_CATALOG_ADD_TO_NEW));
bool current = (current_track->index == viewer.current_playing_track);
- result = do_menu(&menu_items, NULL);
+ result = do_menu(&playlist_viewer_menu_items, NULL);
if (result == MENU_ATTACHED_USB)
{
ret = -1;
@@ -497,15 +497,15 @@
}
/* Menu of viewer options. Invoked via F1(r) or Menu(p). */
-MENUITEM_SETTING(show_icons, &global_settings.playlist_viewer_icons, NULL);
-MENUITEM_SETTING(show_indices, &global_settings.playlist_viewer_indices, NULL);
-MENUITEM_SETTING(track_display,
+MENUITEM_SETTING(vo_show_icons, &global_settings.playlist_viewer_icons, NULL);
+MENUITEM_SETTING(vo_show_indices, &global_settings.playlist_viewer_indices, NULL);
+MENUITEM_SETTING(vo_track_display,
&global_settings.playlist_viewer_track_display, NULL);
-MENUITEM_FUNCTION(save_playlist_item, 0, ID2P(LANG_SAVE_DYNAMIC_PLAYLIST),
+MENUITEM_FUNCTION(vo_save_playlist_item, 0, ID2P(LANG_SAVE_DYNAMIC_PLAYLIST),
save_playlist_func, 0, NULL, Icon_NOICON);
MAKE_MENU(viewer_settings_menu, ID2P(LANG_PLAYLISTVIEWER_SETTINGS),
NULL, Icon_Playlist,
- &show_icons, &show_indices, &track_display, &save_playlist_item);
+ &vo_show_icons, &vo_show_indices, &vo_track_display, &vo_save_playlist_item);
static bool viewer_menu(void)
{
return do_menu(&viewer_settings_menu, NULL) == MENU_ATTACHED_USB;
@@ -805,3 +805,4 @@
return ret;
}
+
Index: apps/plugin.c
===================================================================
--- apps/plugin.c (revision 14960)
+++ apps/plugin.c (working copy)
@@ -514,6 +514,8 @@
flush_icache,
invalidate_icache,
#endif
+
+ get_menu_call_stats_list,
};
int plugin_load(const char* plugin, void* parameter)
Index: apps/plugin.h
===================================================================
--- apps/plugin.h (revision 14960)
+++ apps/plugin.h (working copy)
@@ -112,7 +112,7 @@
#define PLUGIN_MAGIC 0x526F634B /* RocK */
/* increase this every time the api struct changes */
-#define PLUGIN_API_VERSION 78
+#define PLUGIN_API_VERSION 79
/* update this to latest version if a change to the api struct breaks
backwards compatibility (and please take the opportunity to sort in any
@@ -632,6 +632,9 @@
void (*flush_icache)(void);
void (*invalidate_icache)(void);
#endif
+ /* Statistics about the menu item selection (array of addresses of stat structs
+ * and the number of elements in the array) */
+ void (*get_menu_call_stats_list)(struct s_call_stats * const **list, int *list_length);
};
/* plugin header */
Index: apps/plugins/CATEGORIES
===================================================================
--- apps/plugins/CATEGORIES (revision 14960)
+++ apps/plugins/CATEGORIES (working copy)
@@ -36,6 +36,7 @@
maze,games
mazezam,games
mem_mon,apps
+menu_call_stats,apps
metronome,apps
midi2wav,viewers
midiplay,viewers
Index: apps/plugins/menu_call_stats.c
===================================================================
--- apps/plugins/menu_call_stats.c (revision 0)
+++ apps/plugins/menu_call_stats.c (revision 0)
@@ -0,0 +1,392 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id: dice.c 13984 2007-07-25 13:08:12Z jdgordon $
+ *
+ * Copyright (c) 2005 by Alexander Levin
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/*
+ * Plugin for loading of menu statistics from or writing them to a file
+ */
+
+#include "plugin.h"
+
+PLUGIN_HEADER
+
+#define FILENAME "/.rockbox/menustats.mnu"
+
+static struct plugin_api* rb;
+MEM_FUNCTION_WRAPPERS(rb);
+
+#define ERR_NO_FILE -1
+#define ERR_UNKNOWN_ID -2
+#define ERR_FILE_NOT_COMPLETE -3
+#define ERR_BAD_RB_STATE -4
+#define ERR_BAD_ITEM_CALL_CNT -5
+#define ERR_BAD_SYNTAX -6
+#define ERR_OTHER -99
+
+#define CONTROL_PREFIX "#"
+#define CONTROL_PREFIX_LEN 1 /* strlen(CONTROL_PREFIX) */
+#define NAME_VALUE_SEPARATOR ":"
+#define NAME_VALUE_SEPARATOR_LEN 1 /* strlen(NAME_VALUE_SEPARATOR) */
+
+#define FULL_COVERAGE "full coverage"
+
+typedef struct s_call_stats * const *list_type;
+
+/* Whether all items must have been covered when loading statistics */
+static bool enforce_full_coverage;
+
+static int dump_stats(char *filename)
+{
+ list_type list;
+ int size;
+ struct s_call_stats *stats;
+ int i;
+ int fd;
+
+ fd = rb->creat(filename);
+ if (fd < 0) {
+ rb->splash(HZ*2, "Could not open %s for writing", filename);
+ return ERR_NO_FILE;
+ }
+
+ rb->get_menu_call_stats_list(&list, &size);
+
+ for (i=0; ifdprintf(fd, "%s: %d\n", stats->item_id, stats->call_count);
+ }
+
+ rb->close(fd);
+ DEBUGF("Dumped %d entries to the file '%s'\n", size, filename);
+
+ return size;
+}
+
+/* Returns the pointer to the stats for the item with the specified id
+ or NULL if not found */
+static struct s_call_stats *find_item(char *id, list_type list, int size)
+{
+ struct s_call_stats *item;
+ int i;
+ for (i=0; istrcmp(id, item->item_id) == 0)
+ return item;
+ }
+ return NULL;
+}
+
+/* Replaces the counter according to new = -(old+1)
+ * This transformation has two properties:
+ * 1. If the old value is not negative the new value is guaranteed
+ * to be negative
+ * 2. After another transformation, old value is restored
+ */
+static void replace_call_counter(struct s_call_stats *item)
+{
+ item->call_count = -(item->call_count+1);
+}
+
+static void replace_call_counters(list_type list, int size,
+ bool only_negative)
+{
+ struct s_call_stats *stats;
+ int i;
+ for (i=0; icall_count >= 0 && only_negative))
+ stats->call_count = -(stats->call_count+1);
+ }
+}
+
+/* Checks that all counters are non negative */
+static bool has_negative_counters(list_type list, int size)
+{
+ int i;
+ for (i=0; icall_count < 0) {
+ DEBUGF("Negative RB counter for entry '%s'\n", list[i]->item_id);
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Eats spaces at the end of line */
+static void rtrim(char *line)
+{
+ char *p = line + rb->strlen(line) - 1;
+ while ((*p == ' ') && (p >= line))
+ *(p--) = '\0';
+}
+
+
+static char *skip_spaces(char *p)
+{
+ while (*p == ' ')
+ p++;
+ return p;
+}
+
+
+static bool parse_name_value(char *line, char *name, int namesize,
+ char *value, int valuesize)
+{
+ char *sep;
+ int name_len, val_len;
+ name[0] = value[0] = '\0';
+
+ line = skip_spaces(line);
+
+ sep = rb->strcasestr(line, NAME_VALUE_SEPARATOR);
+ if (sep == NULL) {
+ /* No separator char -> weird instruction */
+ return false;
+ }
+ name_len = sep - line;
+ if (name_len >= namesize) {
+ /* Too long name */
+ return false;
+ }
+ rb->strncpy(name, line, name_len);
+ name[name_len] = '\0';
+
+ sep += NAME_VALUE_SEPARATOR_LEN;
+ sep = skip_spaces(sep);
+ val_len = rb->strlen(line) - (sep - line);
+ if (val_len >= valuesize) {
+ /* Too long value */
+ return false;
+ }
+ rb->strncpy(value, sep, val_len+1);
+
+ rtrim(name);
+ rtrim(value);
+
+ DEBUGF("Parsed '%s': '%s'='%s'\n", line, name, value);
+
+ return true;
+}
+
+static bool parse_entry(char *line, char *item_id, int *item_counter)
+{
+ char cnt_str[20];
+ if (!parse_name_value(line, item_id, 100, cnt_str, 20))
+ return false;
+
+ if (rb->strlen(cnt_str) == 0)
+ return false; /* No value */
+
+ *item_counter = rb->atoi(cnt_str);
+ return true;
+}
+
+static bool is_control(char *line)
+{
+ char name[100], value[100];
+ if (rb->strncmp(line, CONTROL_PREFIX, CONTROL_PREFIX_LEN) != 0) {
+ return false;
+ }
+ line += CONTROL_PREFIX_LEN;
+
+ if (!parse_name_value(line, name, sizeof(name),
+ value, sizeof(value))) {
+ DEBUGF("Bad processing instruction: '%s'\n", line);
+ return true;
+ }
+
+ /* Process control instruction */
+ if (rb->strcasestr(name, FULL_COVERAGE)) {
+ enforce_full_coverage = (rb->strcasecmp(value, "no") != 0);
+ DEBUGF("Enforce full coverage: %d\n", enforce_full_coverage);
+ } else {
+ /* Unknown instruction -> ignore */
+ DEBUGF("Unknown processing instruction: '%s'\n", name);
+ }
+
+ return true;
+}
+
+
+/* Loads stats from the file. Returns the number of entries read (>=0 ) if
+ everything wenk OK or a negative number in the case of an error.
+
+ Following errors are detected:
+ -1 : file could not be opened for reading
+ -2 : unknown entry id in the file
+ -3 : file contains entries not for all menu items
+ -4 : Rockbox state is not consistent
+ -5 : entry has a negative counter in the file
+ -6 : bad entry syntax
+ -99 : other errors
+ */
+static int load_stats(char *filename)
+{
+ list_type list;
+ int size;
+ struct s_call_stats *stats;
+ int i;
+ int fd;
+ char file_line[100];
+ char nc;
+ char item_id[100];
+ int item_counter;
+ bool file_opened = false;
+ bool cnt_replaced = false;
+ int ret_val;
+
+ rb->get_menu_call_stats_list(&list, &size);
+
+ /* Check that all entries have non-negative counts (should alway be).
+ Only then can we safely do some tricks (see later) */
+ if (has_negative_counters(list, size)) {
+ ret_val = ERR_BAD_RB_STATE;
+ goto end_proc;
+ }
+
+ fd = rb->open(filename, O_RDONLY);
+ if (fd < 0) {
+ rb->splash(HZ*2, "Could not open file %s for reading", filename);
+ ret_val = ERR_NO_FILE;
+ goto end_proc;
+ }
+ file_opened = true;
+
+ replace_call_counters(list, size, false);
+ cnt_replaced = true;
+
+ enforce_full_coverage = true; /* Enforce it by default */
+
+ /* Now read the file and check that the sets in RB and in
+ the file are the same */
+
+ /* First pass: check that all entries are known and have valid values
+ and that all RB entries are covered */
+ while ((nc=rb->read_line(fd, file_line, sizeof(file_line)))) {
+ if (is_control(file_line)) {
+ continue;
+ }
+ if (!parse_entry(file_line, item_id, &item_counter)) {
+ /* Could not parse the entry */
+ DEBUGF("Could not parse '%s'\n", file_line);
+ ret_val = ERR_BAD_SYNTAX;
+ goto end_proc;
+ }
+ stats = find_item(item_id, list, size);
+ if (stats == NULL) {
+ DEBUGF("Unknown item '%s'\n", item_id);
+ ret_val = ERR_UNKNOWN_ID;
+ goto end_proc;
+ }
+ if (item_counter < 0) {
+ DEBUGF("Negative counter for '%s'\n", item_id);
+ ret_val = ERR_BAD_ITEM_CALL_CNT;
+ goto end_proc;
+ }
+ /* Item was OK, convert it back thus marking it as covered */
+ replace_call_counter(stats);
+ }
+
+ /* Check that all items have been covered */
+ if (enforce_full_coverage) {
+ for (i=0; icall_count < 0) {
+ DEBUGF("Item '%s' is not covered\n", stats->item_id);
+ ret_val = ERR_FILE_NOT_COMPLETE;
+ goto end_proc;
+ }
+ }
+ } else {
+ /* Restore the old values of the uncovered items */
+ replace_call_counters(list, size, true);
+ cnt_replaced = false;
+ }
+
+ /* Now we start the second pass and really replace values */
+ rb->lseek(fd, 0, SEEK_SET);
+ while ((nc=rb->read_line(fd, file_line, sizeof(file_line)))) {
+ if (is_control(file_line)) {
+ continue;
+ }
+ if (!parse_entry(file_line, item_id, &item_counter)) {
+ /* Could not parse the entry */
+ DEBUGF("Could not parse '%s'\n", file_line);
+ ret_val = ERR_OTHER;
+ goto end_proc;
+ }
+ stats = find_item(item_id, list, size);
+ if (stats == NULL) {
+ DEBUGF("Unknown item '%s'\n", item_id);
+ ret_val = ERR_OTHER;
+ goto end_proc;
+ }
+ if (item_counter < 0) {
+ DEBUGF("Negative counter for '%s'\n", item_id);
+ ret_val = ERR_OTHER;
+ goto end_proc;
+ }
+ stats->call_count = item_counter;
+ }
+ ret_val = size;
+
+end_proc:
+ if (file_opened) {
+ rb->close(fd);
+ file_opened = false;
+ }
+ if ((ret_val < 0) && cnt_replaced) {
+ replace_call_counters(list, size, true);
+ cnt_replaced = false;
+ }
+ DEBUGF("Loaded entries from the file '%s', ret_val=%d\n",
+ filename, ret_val);
+
+ return ret_val;
+}
+
+/* plugin entry point */
+enum plugin_status plugin_start(struct plugin_api* api, void* parameter) {
+ char *filename;
+ int size;
+
+ rb = api;
+
+ if (parameter == NULL) {
+ /* No file specified -> dump */
+ size = dump_stats(FILENAME);
+ if (size >= 0) {
+ rb->splash(HZ*2, "Saved statistics for %d entries", size);
+ return PLUGIN_OK;
+ } else {
+ rb->splash(HZ*2, "Could not save statistics, error code=%d", size);
+ return PLUGIN_ERROR;
+ }
+ } else {
+ filename = (char*) parameter;
+ size = load_stats(filename);
+ if (size >= 0) {
+ rb->splash(HZ*2, "Loaded statistics for %d entries", size);
+ return PLUGIN_OK;
+ } else {
+ rb->splash(HZ*2, "Could not load statistics from %s, error code=%d",
+ filename, size);
+ return PLUGIN_ERROR;
+ }
+ }
+}
Index: apps/plugins/SOURCES
===================================================================
--- apps/plugins/SOURCES (revision 14960)
+++ apps/plugins/SOURCES (working copy)
@@ -7,6 +7,7 @@
firmware_flash.c
jackpot.c
logo.c
+menu_call_stats.c
mosaique.c
properties.c
random_folder_advance_config.c
Index: apps/plugins/viewers.config
===================================================================
--- apps/plugins/viewers.config (revision 14960)
+++ apps/plugins/viewers.config (working copy)
@@ -38,3 +38,5 @@
ssg,games/superdom,-
link,viewers/shortcuts_view,-
*,viewers/shortcuts_append,-
+mnu,apps/menu_call_stats,-
+mnu,apps/menu_call_stats,-
Index: apps/root_menu.c
===================================================================
--- apps/root_menu.c (revision 14960)
+++ apps/root_menu.c (working copy)
@@ -279,12 +279,13 @@
bookmark_mrb_load();
return GO_TO_PREVIOUS;
}
+
+MENUITEM_STRINGLIST(plugins_menu_items, ID2P(LANG_PLUGINS), NULL,
+ ID2P(LANG_PLUGIN_GAMES),
+ ID2P(LANG_PLUGIN_APPS), ID2P(LANG_PLUGIN_DEMOS));
static int plugins_menu(void* param)
{
(void)param;
- MENUITEM_STRINGLIST(plugins_menu_items, ID2P(LANG_PLUGINS), NULL,
- ID2P(LANG_PLUGIN_GAMES),
- ID2P(LANG_PLUGIN_APPS), ID2P(LANG_PLUGIN_DEMOS));
char *folder;
int retval = GO_TO_PREVIOUS;
int selection = 0, current = 0;
Index: apps/settings.h
===================================================================
--- apps/settings.h (revision 14960)
+++ apps/settings.h (working copy)
@@ -752,6 +752,8 @@
int usb_stack_mode; /* device or host */
unsigned char usb_stack_device_driver[32]; /* usb device driver to load */
#endif
+ /* Reordering of menu items */
+ bool menu_auto_reorder;
};
/** global variables **/
Index: apps/settings_list.c
===================================================================
--- apps/settings_list.c (revision 14960)
+++ apps/settings_list.c (working copy)
@@ -1281,6 +1281,8 @@
FILENAME_SETTING(0, usb_stack_device_driver, "usb device driver",
"storage", NULL, NULL, 32),
#endif /* HAVE_USBSTACK */
+ OFFON_SETTING(0, menu_auto_reorder, LANG_MENU_AUTO_REORDERING, false,
+ "auto menu reordering", NULL),
};
const int nb_settings = sizeof(settings)/sizeof(*settings);
Index: apps/SOURCES
===================================================================
--- apps/SOURCES (revision 14960)
+++ apps/SOURCES (working copy)
@@ -158,3 +158,4 @@
#elif CONFIG_KEYPAD == MROBE500_PAD
keymaps/keymap-mr500.c
#endif
+menu_call_stats_list.c