Index: apps/SOURCES
===================================================================
RCS file: /cvsroot/rockbox/apps/SOURCES,v
retrieving revision 1.53
diff -u -r1.53 SOURCES
--- apps/SOURCES	19 Oct 2006 09:42:18 -0000	1.53
+++ apps/SOURCES	23 Oct 2006 20:15:02 -0000
@@ -30,6 +30,9 @@
 tagtree.c
 filetree.c
 scrobbler.c
+#if CONFIG_KEYPAD == IPOD_4G_PAD && !defined(IPOD_MINI)
+ipod_scroll_wheel_gui.c
+#endif
 
 screen_access.c
 gui/buttonbar.c
Index: apps/ipod_scroll_wheel_gui.c
===================================================================
RCS file: apps/ipod_scroll_wheel_gui.c
diff -N apps/ipod_scroll_wheel_gui.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ apps/ipod_scroll_wheel_gui.c	23 Oct 2006 20:15:02 -0000
@@ -0,0 +1,164 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ *
+ * Copyright (C) TP Diffenbach (2006)
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#include "config.h"
+
+#if CONFIG_KEYPAD == IPOD_4G_PAD && !defined(IPOD_MINI)
+
+#include "button.h"
+#include "settings.h"
+#include "logf.h"
+#include "list.h"
+#include "screen_access.h"
+#include "statusbar.h"
+#include "ipod_scroll_wheel_gui.h"
+
+ struct scroll_accel_jump {
+    enum { NOACCEL, PAGE, PERCENT } jump_type ;
+    unsigned int amount ;
+    unsigned int lines ;
+} ;
+
+struct scroll_accel_jump scroll_accel_jumps[] = { { NOACCEL, 0, 0 }, { PAGE, 1, 0  }, { PAGE, 8, 0  } , { PERCENT, 10, 0 } } ;
+
+static void calculate_jump_lines( struct scroll_accel_jump* j, unsigned int list_items, unsigned int screen_lines ) {
+    j->lines = j->jump_type == PERCENT 
+        ? ( j->amount * list_items ) / 100 
+        :  j->amount * screen_lines ;
+}
+
+static inline unsigned int list_items( struct gui_list* list ) {
+    return list->nb_items ;
+}
+
+static inline unsigned int screen_lines( struct gui_list* list ) {
+    return list->display->nb_lines ;
+}
+
+static struct scroll_accel_jump* find_accel_for_list( 
+        struct gui_list* list, struct scroll_accel_jump* jump, unsigned int raw_accel ) {
+    unsigned int l_items = list_items( list ) ;
+    unsigned int s_lines = screen_lines( list ) ;
+
+    /* never accel in a list <= page long */
+    if( raw_accel == 0 || l_items <= s_lines ) {
+        return jump ;
+    }
+
+    /* otherwise, find the highest possible accel  */        
+    calculate_jump_lines( jump + raw_accel, l_items, s_lines ) ;
+
+    while( raw_accel > 1 ) {
+        calculate_jump_lines( jump + raw_accel - 1, l_items, s_lines ) ;
+        if( jump[ raw_accel ].lines > jump[ raw_accel - 1 ].lines ) break ;
+        --raw_accel ;
+    }
+    return jump + raw_accel ;    
+}
+
+static void display_ipod_scroll_wheel_acceration( int accel, int speed ) {
+    static int previous_accel = -1 ;
+    static int previous_speed = 0 ;
+     if( previous_accel != accel || previous_speed != speed ) {
+        gui_statusbar_draw_scroll_accel( &screens[ SCREEN_MAIN ], accel, speed ) ;
+        previous_accel = accel ;
+        previous_speed = speed ;
+    }
+}
+
+
+/* public functions declared in header */
+/* these next two really belong in list.c */
+void gui_synclist_select_forward_n_lines( struct gui_synclist* lists, unsigned int lines )
+{
+    int i;
+    FOR_NB_SCREENS(i)
+        gui_list_select_next_page(&(lists->gui_list[i]), lines );
+}
+
+void gui_synclist_select_back_n_lines( struct gui_synclist* lists, unsigned int lines )
+{
+    int i;
+    FOR_NB_SCREENS(i)
+        gui_list_select_previous_page(&(lists->gui_list[i]), lines);
+}
+
+
+/* this goes away in production, I think */
+static get_ipod_scroll_wheel_acceration_and_cps( int* speed ) {
+    int cps = get_ipod_scroll_wheel_clicks_per_second() ;
+    *speed = cps ;
+    unsigned int acps = cps < 0 ? -cps : cps ;
+    int a =  acps < global_settings.ipod_scroll_wheel_acceleration_fast
+        ? 0
+        : ( acps < global_settings.ipod_scroll_wheel_acceleration_faster 
+        ? 1
+        : ( acps < global_settings.ipod_scroll_wheel_acceleration_fastest
+        ? 2
+        : 3 ) ) ;
+#ifdef ROCKBOX_HAS_LOGF
+    logf( "g:%d,%d", cps, a ) ;
+#endif
+    return a ;
+}
+
+
+/* dispatch the correct synclist function depending on scroll acceleration factor */
+void gui_synclist_handle_accel( struct gui_synclist * lists, enum screen_type screen, enum list_dir dir)
+{
+    int cps ;
+    struct scroll_accel_jump* jump = find_accel_for_list( 
+        &(lists->gui_list[ screen ] ), scroll_accel_jumps, get_ipod_scroll_wheel_acceration_and_cps( &cps ) ) ;
+
+    if( jump->jump_type == NOACCEL ) {
+        if( dir == FORWARD )
+            gui_synclist_select_next( lists ) ;
+        else
+            gui_synclist_select_previous( lists ) ;
+    } else {
+        if( dir == FORWARD )
+            gui_synclist_select_forward_n_lines( lists, jump->lines ) ;
+        else
+            gui_synclist_select_back_n_lines( lists, jump->lines ) ;
+    }
+
+    display_ipod_scroll_wheel_acceration( jump - scroll_accel_jumps, cps ) ;
+}
+
+ unsigned int get_ipod_scroll_wheel_acceration( void ) {
+    int cps = get_ipod_scroll_wheel_clicks_per_second() ;
+
+    unsigned int acps = cps < 0 ? -cps : cps ;
+    int a =  acps < global_settings.ipod_scroll_wheel_acceleration_fast
+        ? 0
+        : ( acps < global_settings.ipod_scroll_wheel_acceleration_faster 
+        ? 1
+        : ( acps < global_settings.ipod_scroll_wheel_acceleration_fastest
+        ? 2
+        : 3 ) ) ;
+
+    return a ;
+ }
+
+unsigned int get_and_display_ipod_scroll_wheel_acceration( void ) {
+    int cps ;
+    int a = get_ipod_scroll_wheel_acceration_and_cps( &cps ) ;
+    display_ipod_scroll_wheel_acceration( a, cps ) ;
+    return a ;
+}
+#endif
Index: apps/ipod_scroll_wheel_gui.h
===================================================================
RCS file: apps/ipod_scroll_wheel_gui.h
diff -N apps/ipod_scroll_wheel_gui.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ apps/ipod_scroll_wheel_gui.h	23 Oct 2006 20:15:02 -0000
@@ -0,0 +1,43 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ *
+ * Copyright (C) TP Diffenbach (2006)
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef _IPOD_SCROLL_WHEEL_GUI_H_
+#define _IPOD_SCROLL_WHEEL_GUI_H_
+#include "config.h"
+
+#if CONFIG_KEYPAD == IPOD_4G_PAD && !defined(IPOD_MINI)
+#include "list.h"
+#include "screen_access.h"
+
+extern void gui_synclist_select_forward_n_lines(struct gui_synclist* lists, unsigned int lines ) ;
+extern void gui_synclist_select_back_n_lines(struct gui_synclist* lists, unsigned int lines ) ;
+
+enum list_dir { BACKWARD, FORWARD } ;
+/* dispatch the correct synclist function depending on scroll acceleration factor 
+ * may adjust the actual accelration depending on list length
+ * displays adjusted acceleration to the statusbar (if the statusbar is on, clients don't need to check)
+*/
+extern void gui_synclist_handle_accel( struct gui_synclist* lists, enum screen_type screen, enum list_dir dir) ;
+
+/* returns the accelreation */
+extern unsigned int get_ipod_scroll_wheel_acceration( void ) ;
+
+/* returns the accelreation and displays it to the statusbar (if the statusbar is on, clients don't need to check) */
+extern unsigned int get_and_display_ipod_scroll_wheel_acceration( void ) ;
+
+#endif
+#endif
Index: apps/settings.c
===================================================================
RCS file: /cvsroot/rockbox/apps/settings.c,v
retrieving revision 1.430
diff -u -r1.430 settings.c
--- apps/settings.c	23 Oct 2006 12:35:10 -0000	1.430
+++ apps/settings.c	23 Oct 2006 20:15:04 -0000
@@ -667,6 +667,12 @@
 
     {1, S_O(audioscrobbler), false, "Last.fm Logging", off_on},
 
+#if (CONFIG_KEYPAD == IPOD_4G_PAD) && !defined(IPOD_MINI)
+    {9, S_O(ipod_scroll_wheel_acceleration_fast), 135, "ipod scroll wheel fast threshold in clicks/sec", NULL }, /* 0...511 */
+    {9, S_O(ipod_scroll_wheel_acceleration_faster), 255, "ipod scroll wheel faster threshold in clicks/sec", NULL }, /* 0...511 */
+    {9, S_O(ipod_scroll_wheel_acceleration_fastest), 425, "ipod scroll wheel fastest threshold in clicks/sec", NULL }, /* 0...511 */
+#endif
+
     /* If values are just added to the end, no need to bump the version. */
     /* new stuff to be added at the end */
 
Index: apps/settings.h
===================================================================
RCS file: /cvsroot/rockbox/apps/settings.h,v
retrieving revision 1.248
diff -u -r1.248 settings.h
--- apps/settings.h	19 Oct 2006 09:42:18 -0000	1.248
+++ apps/settings.h	23 Oct 2006 20:15:05 -0000
@@ -500,6 +500,11 @@
 #endif
     bool audioscrobbler; /* Audioscrobbler logging  */
 
+#if (CONFIG_KEYPAD == IPOD_4G_PAD) && !defined(IPOD_MINI)
+    unsigned int ipod_scroll_wheel_acceleration_fast ;
+    unsigned int ipod_scroll_wheel_acceleration_faster ;
+    unsigned int ipod_scroll_wheel_acceleration_fastest ;
+#endif
 };
 
 enum optiontype { INT, BOOL };
Index: apps/settings_menu.c
===================================================================
RCS file: /cvsroot/rockbox/apps/settings_menu.c,v
retrieving revision 1.280
diff -u -r1.280 settings_menu.c
--- apps/settings_menu.c	22 Oct 2006 07:51:03 -0000	1.280
+++ apps/settings_menu.c	23 Oct 2006 20:15:07 -0000
@@ -2169,6 +2169,7 @@
     return result;
 }
 
+static bool ipod_scroll_wheel_acceleration_by_speed_menu( void ) ;
 
 static bool system_settings_menu(void)
 {
@@ -2195,6 +2196,7 @@
 #ifdef CONFIG_CHARGING
         { ID2P(LANG_CAR_ADAPTER_MODE), car_adapter_mode       },
 #endif
+        { ID2P(LANG_IPOD_SCROLL_WHEEL_SPEED), ipod_scroll_wheel_acceleration_by_speed_menu       },
     };
 
     m=menu_init( items, sizeof(items) / sizeof(*items), NULL,
@@ -2225,3 +2227,45 @@
     menu_exit(m);
     return result;
 }
+
+static bool ipod_scroll_wheel_set_acceleration_by_speed( int* var, const unsigned char* desc )
+{
+    return set_int( desc, "s", UNIT_SEC, var,
+                   NULL, 5, 1, 500, NULL );
+}
+
+static bool ipod_scroll_wheel_acceleration_by_speed_fast( void )
+{
+    return ipod_scroll_wheel_set_acceleration_by_speed( 
+      &global_settings.ipod_scroll_wheel_acceleration_fast,
+      str(LANG_IPOD_SCROLL_WHEEL_FAST) ) ;
+}
+
+static bool ipod_scroll_wheel_acceleration_by_speed_faster( void )
+{
+    return ipod_scroll_wheel_set_acceleration_by_speed(
+      &global_settings.ipod_scroll_wheel_acceleration_faster,
+      str(LANG_IPOD_SCROLL_WHEEL_FASTER) ) ;
+}
+
+static bool ipod_scroll_wheel_acceleration_by_speed_fastest( void )
+{
+    return ipod_scroll_wheel_set_acceleration_by_speed(
+      &global_settings.ipod_scroll_wheel_acceleration_fastest,
+      str(LANG_IPOD_SCROLL_WHEEL_FASTEST) ) ;
+}
+
+static bool ipod_scroll_wheel_acceleration_by_speed_menu( void ) 
+{
+    struct menu_item items[] = {
+        { ID2P(LANG_IPOD_SCROLL_WHEEL_FAST),      ipod_scroll_wheel_acceleration_by_speed_fast },
+        { ID2P(LANG_IPOD_SCROLL_WHEEL_FASTER),      ipod_scroll_wheel_acceleration_by_speed_faster },
+        { ID2P(LANG_IPOD_SCROLL_WHEEL_FASTEST),      ipod_scroll_wheel_acceleration_by_speed_fastest },
+    };
+
+    int m=menu_init( items, sizeof(items) / sizeof(*items), NULL,
+                 NULL, NULL, NULL);
+    bool result = menu_run(m);
+    menu_exit(m);
+    return result;
+}
Index: apps/gui/list.c
===================================================================
RCS file: /cvsroot/rockbox/apps/gui/list.c,v
retrieving revision 1.34
diff -u -r1.34 list.c
--- apps/gui/list.c	25 Sep 2006 11:06:54 -0000	1.34
+++ apps/gui/list.c	23 Oct 2006 20:15:08 -0000
@@ -33,6 +33,10 @@
 #include "statusbar.h"
 #include "textarea.h"
 
+#if CONFIG_KEYPAD == IPOD_4G_PAD && !defined(IPOD_MINI)
+#include "ipod_scroll_wheel_gui.h"
+#endif
+
 #ifdef HAVE_LCD_CHARCELLS
 #define SCROLL_LIMIT 1
 #else
@@ -733,7 +737,11 @@
             gui_synclist_limit_scroll(lists, false);
 
         case ACTION_STD_PREVREPEAT:
+#if CONFIG_KEYPAD == IPOD_4G_PAD && !defined(IPOD_MINI)
+            gui_synclist_handle_accel( lists, SCREEN_MAIN, BACKWARD ) ;
+#else
             gui_synclist_select_previous(lists);
+#endif
             gui_synclist_draw(lists);
             yield();
             return ACTION_STD_PREV;
@@ -742,7 +750,11 @@
             gui_synclist_limit_scroll(lists, false);
 
         case ACTION_STD_NEXTREPEAT:
+#if CONFIG_KEYPAD == IPOD_4G_PAD && !defined(IPOD_MINI)
+            gui_synclist_handle_accel( lists, SCREEN_MAIN, FORWARD ) ;
+#else
             gui_synclist_select_next(lists);
+#endif
             gui_synclist_draw(lists);
             yield();
             return ACTION_STD_NEXT;
Index: apps/gui/list.h
===================================================================
RCS file: /cvsroot/rockbox/apps/gui/list.h,v
retrieving revision 1.26
diff -u -r1.26 list.h
--- apps/gui/list.h	23 Aug 2006 20:02:06 -0000	1.26
+++ apps/gui/list.h	23 Oct 2006 20:15:08 -0000
@@ -225,6 +225,16 @@
 extern void gui_list_select_previous_page(struct gui_list * gui_list,
                                           int nb_lines);
 
+ /*
+ * Advance/retreat in the list by n percent of the total list elements,
+ * by at least one line if percent_lines != 0
+ * - gui_list : the list structure
+ * - percent_lines : the percent of the total number of lines to try to move the cursor
+ *   postive adavnces forward, negative retreats back, 0 has no effect
+ */
+extern void gui_list_select_relative_percent( struct gui_list * gui_list,
+                                                int percent_lines ) ;
+
 /*
  * Adds an item to the list (the callback will be asked for one more item)
  * - gui_list : the list structure
Index: apps/gui/statusbar.c
===================================================================
RCS file: /cvsroot/rockbox/apps/gui/statusbar.c,v
retrieving revision 1.32
diff -u -r1.32 statusbar.c
--- apps/gui/statusbar.c	26 Sep 2006 11:56:58 -0000	1.32
+++ apps/gui/statusbar.c	23 Oct 2006 20:15:09 -0000
@@ -115,6 +115,20 @@
                                                 7*ICONS_SPACING
 #define STATUSBAR_LOCKR_WIDTH                   5
 
+#if CONFIG_KEYPAD == IPOD_4G_PAD && !defined(IPOD_MINI)
+#define STATUSBAR_SCROLL_ACCEL_X_POS                   STATUSBAR_X_POS + \
+                                                STATUSBAR_BATTERY_WIDTH + \
+                                                STATUSBAR_PLUG_WIDTH + \
+                                                STATUSBAR_VOLUME_WIDTH + \
+                                                STATUSBAR_PLAY_STATE_WIDTH + \
+                                                STATUSBAR_PLAY_MODE_WIDTH + \
+                                                STATUSBAR_SHUFFLE_WIDTH + \
+                                                STATUSBAR_LOCKM_WIDTH + \
+                                                STATUSBAR_LOCKR_WIDTH + \
+                                                8*ICONS_SPACING
+#define STATUSBAR_SCROLL_ACCEL_WIDTH          48
+#endif
+
 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
 #define STATUSBAR_DISK_WIDTH                    12
 #define STATUSBAR_DISK_X_POS(statusbar_width)   statusbar_width - \
@@ -237,7 +251,14 @@
         memcmp(&(bar->info), &(bar->lastinfo), sizeof(struct status_info)))
     {
         display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
+#if CONFIG_KEYPAD == IPOD_4G_PAD && !defined(IPOD_MINI)
+        display->fillrect(0, 0, STATUSBAR_SCROLL_ACCEL_X_POS, STATUSBAR_HEIGHT);
+        display->fillrect(STATUSBAR_SCROLL_ACCEL_X_POS + STATUSBAR_SCROLL_ACCEL_WIDTH, 0, 
+            display->width-(STATUSBAR_SCROLL_ACCEL_X_POS + STATUSBAR_SCROLL_ACCEL_WIDTH),
+            STATUSBAR_HEIGHT);
+#else
         display->fillrect(0, 0, display->width, STATUSBAR_HEIGHT);
+#endif
         display->set_drawmode(DRMODE_SOLID);
 
 #else
@@ -256,11 +277,11 @@
                                  STATUSBAR_PLUG_X_POS,
                                  STATUSBAR_Y_POS, STATUSBAR_PLUG_WIDTH,
                                  STATUSBAR_HEIGHT);
+
+        else
 #endif /* HAVE_USB_POWER */
+         /* draw power plug if charging */
 #ifdef CONFIG_CHARGING
-#ifdef HAVE_USB_POWER
-        else
-#endif
         /* draw power plug if charging */
         if (bar->info.inserted)
             display->mono_bitmap(bitmap_icons_7x8[Icon_Plug],
@@ -274,7 +295,7 @@
 #endif
             bar->redraw_volume = gui_statusbar_icon_volume(bar, bar->info.volume);
         gui_statusbar_icon_play_state(display, current_playmode() + Icon_Play);
-        
+
 #ifdef HAVE_RECORDING
         /* If in recording screen, replace repeat mode, volume
            and shuffle icons with recording info */
@@ -709,6 +730,34 @@
 }
 #endif /* HAVE_RECORDING */
 
+#if CONFIG_KEYPAD == IPOD_4G_PAD && !defined(IPOD_MINI)
+/*
+ * Print scroll accel to status bar
+ * Note this function is funny; unlike all the others, it's NOT called by gui_statusbar_draw
+ */
+void gui_statusbar_draw_scroll_accel(struct screen* display, int accel, int speed )
+{
+     if(!global_settings.statusbar)
+       return;
+
+    unsigned char buffer[ 10 ];
+    unsigned int width, height;
+    snprintf(buffer, sizeof(buffer), "%d %d", accel, speed );
+    display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
+    display->fillrect(STATUSBAR_SCROLL_ACCEL_X_POS, 0, 
+        STATUSBAR_SCROLL_ACCEL_WIDTH, STATUSBAR_HEIGHT);
+    display->set_drawmode(DRMODE_SOLID);
+    display->setfont(FONT_SYSFIXED);
+    display->getstringsize(buffer, &width, &height);
+    if (height <= STATUSBAR_HEIGHT) {
+        display->putsxy(STATUSBAR_SCROLL_ACCEL_X_POS, STATUSBAR_Y_POS, buffer);
+    }
+    display->setfont(FONT_UI);
+    display->update_rect( STATUSBAR_SCROLL_ACCEL_X_POS, STATUSBAR_Y_POS, 
+        STATUSBAR_SCROLL_ACCEL_WIDTH, STATUSBAR_HEIGHT ) ;
+}
+#endif
+
 #endif /* HAVE_LCD_BITMAP */
 
 void gui_syncstatusbar_init(struct gui_syncstatusbar * bars)
Index: apps/gui/statusbar.h
===================================================================
RCS file: /cvsroot/rockbox/apps/gui/statusbar.h,v
retrieving revision 1.17
diff -u -r1.17 statusbar.h
--- apps/gui/statusbar.h	2 Sep 2006 17:30:30 -0000	1.17
+++ apps/gui/statusbar.h	23 Oct 2006 20:15:09 -0000
@@ -113,6 +113,15 @@
 void gui_statusbar_time(struct screen * display, int hour, int minute);
 #endif
 
+#if CONFIG_KEYPAD == IPOD_4G_PAD && !defined(IPOD_MINI)
+/*
+ * Print scroll accel to status bar
+ * Note this function is funny; unlike all the others, it's NOT called by gui_statusbar_draw
+ * it updtes teh screen on its own,
+ * and it is public so outside clients can call it
+ */
+extern void gui_statusbar_draw_scroll_accel(struct screen* display, int accel, int speed ) ;
+#endif
 
 struct gui_syncstatusbar
 {
Index: apps/lang/english.lang
===================================================================
RCS file: /cvsroot/rockbox/apps/lang/english.lang,v
retrieving revision 1.287
diff -u -r1.287 english.lang
--- apps/lang/english.lang	20 Oct 2006 21:25:45 -0000	1.287
+++ apps/lang/english.lang	23 Oct 2006 20:15:12 -0000
@@ -9974,3 +9974,59 @@
     *: ""
   </voice>
 </phrase>
+<phrase>
+  id: LANG_IPOD_SCROLL_WHEEL_SPEED
+  desc: "Ipod Scroll Wheel Acceleration By Speed" Submenu in "System" menu
+  user:
+  <source>
+    *: "Ipod Scroll Wheel Acceleration"
+  </source>
+  <dest>
+    *: "Ipod Scroll Wheel Acceleration"
+  </dest>
+  <voice>
+    *: "Ipod Scroll Wheel Acceleration"
+  </voice>
+</phrase>
+<phrase>
+  id: LANG_IPOD_SCROLL_WHEEL_FAST
+  desc: "Ipod Scroll Wheel By Speed Fast Threshold" Submenu in "Ipod Scroll Wheel Acceleration By Speed" menu
+  user:
+  <source>
+    *: "Minimum speed for 'fast' acceleration"
+  </source>
+  <dest>
+    *: "Minimum speed for 'fast' acceleration"
+  </dest>
+  <voice>
+    *: "Minimum speed for 'fast' acceleration"
+  </voice>
+</phrase>
+<phrase>
+  id: LANG_IPOD_SCROLL_WHEEL_FASTER
+  desc: "Ipod Scroll Wheel By Speed Faster Threshold" Submenu in "Ipod Scroll Wheel Acceleration By Speed" menu
+  user:
+  <source>
+    *: "Minimum speed for 'faster' acceleration"
+  </source>
+  <dest>
+    *: "Minimum speed for 'faster' acceleration"
+  </dest>
+  <voice>
+    *: "Minimum speed for 'faster' acceleration"
+  </voice>
+</phrase>
+<phrase>
+  id: LANG_IPOD_SCROLL_WHEEL_FASTEST
+  desc: "Ipod Scroll Wheel By Speed Fastest Threshold" Submenu in "Ipod Scroll Wheel Acceleration By Speed" menu
+  user:
+  <source>
+    *: "Minimum speed for 'fastest' acceleration"
+  </source>
+  <dest>
+    *: "Minimum speed for 'fastest' acceleration"
+  </dest>
+  <voice>
+    *: "Minimum speed for 'fastest' acceleration"
+  </voice>
+</phrase>
\ No newline at end of file
Index: firmware/target/arm/ipod/button-clickwheel.c
===================================================================
RCS file: /cvsroot/rockbox/firmware/target/arm/ipod/button-clickwheel.c,v
retrieving revision 1.2
diff -u -r1.2 button-clickwheel.c
--- firmware/target/arm/ipod/button-clickwheel.c	7 Oct 2006 12:19:33 -0000	1.2
+++ firmware/target/arm/ipod/button-clickwheel.c	23 Oct 2006 20:15:14 -0000
@@ -39,6 +39,7 @@
 #include "power.h"
 #include "system.h"
 #include "powermgmt.h"
+#include "logf.h"
 
 /* Variable to use for setting button status in interrupt handler */
 int int_btn = BUTTON_NONE;
@@ -47,6 +48,34 @@
     static bool send_events = true;
 #endif
 
+
+void ipod_4g_button_int( void ) ;
+
+static int accumulated_wheel_scroll_delta = 0 ;
+unsigned long wheel_scroll_down_microsecond_tick = 0 ;
+unsigned long wheel_scroll_key_microsecond_tick = 0 ;
+unsigned long wheel_scroll_key_microsecond_tick_elapsed = 0 ;
+const unsigned long ONE_MILLION = 1000000 ;
+
+int get_accumulated_wheel_scroll_delta( void ) {
+    return accumulated_wheel_scroll_delta ;
+}
+
+int get_ipod_scroll_wheel_clicks_per_second( void ) {
+    unsigned long wst = wheel_scroll_key_microsecond_tick ;
+    int accum = accumulated_wheel_scroll_delta ;
+    unsigned int abs_accum = accum < 0 ? -accum : accum ;
+    unsigned long  wste = wheel_scroll_key_microsecond_tick_elapsed  ;
+    if( wste == wst ) wst = wheel_scroll_down_microsecond_tick ;
+    unsigned long diff = wste - wst ;
+    unsigned int r = diff ? ( ( abs_accum *  ONE_MILLION ) / diff ) : 0 ;
+#ifdef ROCKBOX_HAS_LOGF
+    logf( "d1,%d,%d", wst, wste ) ;
+    logf( "d2,%d,%d,%d", diff, accum, r ) ;
+#endif
+    return accum < 0 ? -r : r ;
+}
+
 static void opto_i2c_init(void)
 {
     int i, curr_value;
@@ -114,38 +143,60 @@
                 int new_wheel_value = (status << 9) >> 25;
                 whl = new_wheel_value;
                 backlight_on();
-                /* The queue should have no other events when scrolling */
-                if (queue_empty(&button_queue) && old_wheel_value >= 0) {
+
+
+                if( old_wheel_value == -1 ) {
+                /* -1 is a sentinal value, meaning there is no old value 
+                 * because the finger was lifted and thus, no key to emit
+                 */
+                    old_wheel_value = new_wheel_value ;
+                    accumulated_wheel_scroll_delta = 0 ;
+                    wheel_scroll_down_microsecond_tick = USEC_TIMER ;
+
+                } else {
+                /* calculate the delta regardless of whether we'll add a key
+                 * for the use of the acceleration fucntion
+                 */
+
+
 
                     /* This is for later = BUTTON_SCROLL_TOUCH;*/
                     int wheel_delta = new_wheel_value - old_wheel_value;
-                    unsigned long data;
-                    int wheel_keycode;
+
 
                     if (wheel_delta < -48)
                         wheel_delta += 96; /* Forward wrapping case */
                     else if (wheel_delta > 48)
                         wheel_delta -= 96; /* Backward wrapping case */
 
-                    if (wheel_delta > 4) {
-                        wheel_keycode = BUTTON_SCROLL_FWD;
-                    } else if (wheel_delta < -4) {
-                        wheel_keycode = BUTTON_SCROLL_BACK;
-                    } else goto wheel_end;
+                    if( wheel_delta > 4 || wheel_delta < -4 ) {
+                    /* ignore anything less than a jiggle factor */
 
 #ifdef HAVE_WHEEL_POSITION
                     if (send_events)
 #endif
                     {
-                    data = (wheel_delta << 16) | new_wheel_value;
-                    queue_post(&button_queue, wheel_keycode | wheel_repeat,
-                            (void *)data);
+                        if ( queue_empty(&button_queue) ) {
+                        /* only post a scroll button if the queue is empty */
+                            int wheel_keycode = wheel_delta > 0 ? BUTTON_SCROLL_FWD : BUTTON_SCROLL_BACK ;
+                            unsigned long data =  (wheel_delta << 16) | new_wheel_value;
+                            wheel_scroll_key_microsecond_tick = wheel_scroll_key_microsecond_tick_elapsed ;
+                            accumulated_wheel_scroll_delta = 0 ;
+
+                            queue_post(&button_queue, wheel_keycode | wheel_repeat, (void *)data);
                     }
 
                     if (!wheel_repeat) wheel_repeat = BUTTON_REPEAT;
                 }
 
-                old_wheel_value = new_wheel_value;
+                        accumulated_wheel_scroll_delta += wheel_delta ;
+                        old_wheel_value = new_wheel_value ;
+                        wheel_scroll_key_microsecond_tick_elapsed = USEC_TIMER ;
+
+                    } else goto wheel_end ; /* not clear this is still needed, basically skips call to opto_i2c_init() */
+                }
+
+
             } else if (old_wheel_value >= 0) {
                 /* scroll wheel up */
                 old_wheel_value = -1;
Index: firmware/target/arm/ipod/button-target.h
===================================================================
RCS file: /cvsroot/rockbox/firmware/target/arm/ipod/button-target.h,v
retrieving revision 1.1
diff -u -r1.1 button-target.h
--- firmware/target/arm/ipod/button-target.h	5 Oct 2006 10:58:51 -0000	1.1
+++ firmware/target/arm/ipod/button-target.h	23 Oct 2006 20:15:14 -0000
@@ -32,6 +32,20 @@
 void ipod_mini_button_int(void);
 void ipod_4g_button_int(void);
 
+extern unsigned long wheel_scroll_tick ;
+extern int get_accumulated_wheel_scroll_delta( void )  ;
+extern int get_ipod_scroll_wheel_clicks_per_second( void ) ;
+//enum wheel_acceleration { ACCEL_NONE, ACCEL_FAST, ACCEL_FASTER, ACCEL_FASTEST } ;
+//extern enum wheel_acceleration get_ipod_scroll_wheel_acceration( unsigned int* pq1, unsigned int* pq1a ) ;
+/* warning to clients calling get_ipod_scroll_wheel_acceration:
+ * Calling this function may have side-effects; 
+ * this function should be called once per call to button_get
+ * or button_get_w_tmo, and results locally cached as needed.
+ * In particular, some acceleration implementations
+ * may need to reset a calculated delta when 
+ * get_ipod_scroll_wheel_acceration is called.
+ */
+
 /* iPod specific button codes */
 
 #define BUTTON_SELECT       0x00000001
