1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2007 by Jonathan Gordon
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 /* This file contains the code to draw the list widget on BITMAP LCDs. */
35 #include "screen_access.h"
37 #include "scrollbar.h"
42 #include "statusbar-skinned.h"
45 #define ICON_PADDING 1
47 /* these are static to make scrolling work */
48 static struct viewport list_text
[NB_SCREENS
], title_text
[NB_SCREENS
];
50 #ifdef HAVE_TOUCHSCREEN
51 /* difference in pixels between draws, above it means enough to start scrolling */
52 #define SCROLL_BEGIN_THRESHOLD 3
55 SCROLL_NONE
, /* no scrolling */
56 SCROLL_BAR
, /* scroll by using the scrollbar */
57 SCROLL_SWIPE
, /* scroll by wiping over the screen */
58 SCROLL_KINETIC
, /* state after releasing swipe */
64 int gui_list_get_item_offset(struct gui_synclist
* gui_list
, int item_width
,
65 int text_pos
, struct screen
* display
,
67 bool list_display_title(struct gui_synclist
*list
, enum screen_type screen
);
69 void gui_synclist_scroll_stop(struct gui_synclist
*lists
)
74 screens
[i
].scroll_stop(&list_text
[i
]);
75 screens
[i
].scroll_stop(&title_text
[i
]);
76 screens
[i
].scroll_stop(lists
->parent
[i
]);
81 internal screen layout:
83 |TI| title | TI is title icon
87 | | | items | I - icons
91 Note: This image is flipped horizontally when the language is a
92 right-to-left one (Hebrew, Arabic)
94 static bool draw_title(struct screen
*display
, struct gui_synclist
*list
)
96 const int screen
= display
->screen_type
;
97 int style
= STYLE_DEFAULT
;
98 struct viewport
*title_text_vp
= &title_text
[screen
];
100 if (sb_set_title_text(list
->title
, list
->title_icon
, screen
))
101 return false; /* the sbs is handling the title */
102 display
->scroll_stop(title_text_vp
);
103 if (!list_display_title(list
, screen
))
105 *title_text_vp
= *(list
->parent
[screen
]);
106 title_text_vp
->height
= font_get(title_text_vp
->font
)->height
;
108 if (list
->title_icon
!= Icon_NOICON
&& global_settings
.show_icons
)
110 struct viewport title_icon
= *title_text_vp
;
112 title_icon
.width
= get_icon_width(screen
) + ICON_PADDING
* 2;
113 if (VP_IS_RTL(&title_icon
))
115 title_icon
.x
+= title_text_vp
->width
- title_icon
.width
;
119 title_text_vp
->x
+= title_icon
.width
;
121 title_text_vp
->width
-= title_icon
.width
;
123 display
->set_viewport(&title_icon
);
124 screen_put_icon(display
, 0, 0, list
->title_icon
);
126 #ifdef HAVE_LCD_COLOR
127 if (list
->title_color
>= 0)
129 style
|= (STYLE_COLORED
|list
->title_color
);
132 display
->set_viewport(title_text_vp
);
133 display
->puts_scroll_style(0, 0, list
->title
, style
);
137 void list_draw(struct screen
*display
, struct gui_synclist
*list
)
139 struct viewport list_icons
;
140 int start
, end
, line_height
, style
, i
;
141 const int screen
= display
->screen_type
;
142 const int list_start_item
= list
->start_item
[screen
];
143 const int icon_width
= get_icon_width(screen
) + ICON_PADDING
;
144 const bool scrollbar_in_left
= (global_settings
.scrollbar
== SCROLLBAR_LEFT
);
145 const bool show_cursor
= !global_settings
.cursor_style
&&
146 list
->show_selection_marker
;
147 struct viewport
*parent
= (list
->parent
[screen
]);
148 #ifdef HAVE_LCD_COLOR
149 unsigned char cur_line
= 0;
153 struct viewport
*list_text_vp
= &list_text
[screen
];
155 line_height
= font_get(parent
->font
)->height
;
156 display
->set_viewport(parent
);
157 display
->clear_viewport();
158 display
->scroll_stop(list_text_vp
);
159 *list_text_vp
= *parent
;
160 if ((show_title
= draw_title(display
, list
)))
162 list_text_vp
->y
+= line_height
;
163 list_text_vp
->height
-= line_height
;
166 const int nb_lines
= viewport_get_nb_lines(list_text_vp
);
168 start
= list_start_item
;
169 end
= start
+ nb_lines
;
171 #ifdef HAVE_TOUCHSCREEN
172 if (list
->selected_item
== 0 || (list
->nb_items
< nb_lines
))
173 y_offset
= 0; /* reset in case it's a new list */
175 int draw_offset
= y_offset
;
176 /* draw some extra items to not have empty lines at the top and bottom */
179 /* make it negative for more consistent apparence when switching
181 draw_offset
-= line_height
;
185 else if (y_offset
< 0)
188 #define draw_offset 0
191 /* draw the scrollbar if its needed */
192 if (global_settings
.scrollbar
&& nb_lines
< list
->nb_items
)
194 struct viewport vp
= *list_text_vp
;
195 vp
.width
= SCROLLBAR_WIDTH
;
196 vp
.height
= line_height
* nb_lines
;
198 list_text_vp
->width
-= SCROLLBAR_WIDTH
;
199 if (scrollbar_in_left
)
200 list_text_vp
->x
+= SCROLLBAR_WIDTH
;
202 vp
.x
+= list_text_vp
->width
;
203 display
->set_viewport(&vp
);
204 gui_scrollbar_draw(display
,
205 (scrollbar_in_left
? 0: 1), 0, SCROLLBAR_WIDTH
-1, vp
.height
,
206 list
->nb_items
, list_start_item
, list_start_item
+ nb_lines
,
211 /* shift everything a bit in relation to the title... */
212 if (!VP_IS_RTL(list_text_vp
) && scrollbar_in_left
)
214 list_text_vp
->width
-= SCROLLBAR_WIDTH
;
215 list_text_vp
->x
+= SCROLLBAR_WIDTH
;
217 else if (VP_IS_RTL(list_text_vp
) && !scrollbar_in_left
)
219 list_text_vp
->width
-= SCROLLBAR_WIDTH
;
223 /* setup icon placement */
224 list_icons
= *list_text_vp
;
225 int icon_count
= (list
->callback_get_item_icon
!= NULL
) ? 1 : 0;
230 list_icons
.width
= icon_width
* icon_count
;
231 list_text_vp
->width
-= list_icons
.width
+ ICON_PADDING
;
232 if (VP_IS_RTL(&list_icons
))
233 list_icons
.x
+= list_text_vp
->width
+ ICON_PADDING
;
235 list_text_vp
->x
+= list_icons
.width
+ ICON_PADDING
;
238 for (i
=start
; i
<end
&& i
<list
->nb_items
; i
++)
241 unsigned const char *s
;
242 char entry_buffer
[MAX_PATH
];
243 unsigned char *entry_name
;
245 int line
= i
- start
;
246 s
= list
->callback_get_item_name(i
, list
->data
, entry_buffer
,
247 sizeof(entry_buffer
));
248 entry_name
= P2STR(s
);
249 display
->set_viewport(list_text_vp
);
250 style
= STYLE_DEFAULT
;
251 /* position the string at the correct offset place */
253 display
->getstringsize(entry_name
, &item_width
, &h
);
254 item_offset
= gui_list_get_item_offset(list
, item_width
, text_pos
,
255 display
, list_text_vp
);
257 #ifdef HAVE_LCD_COLOR
258 /* if the list has a color callback */
259 if (list
->callback_get_item_color
)
261 int color
= list
->callback_get_item_color(i
, list
->data
);
262 /* if color selected */
265 style
|= STYLE_COLORED
|color
;
269 /* draw the selected line */
271 #ifdef HAVE_TOUCHSCREEN
272 /* don't draw it during scrolling */
273 scroll_mode
== SCROLL_NONE
&&
275 i
>= list
->selected_item
276 && i
< list
->selected_item
+ list
->selected_size
277 && list
->show_selection_marker
)
278 {/* The selected item must be displayed scrolling */
279 if (global_settings
.cursor_style
== 1
280 #ifdef HAVE_REMOTE_LCD
281 /* the global_settings.cursor_style check is here to make
282 * sure if they want the cursor instead of bar it will work
284 || (display
->depth
< 16 && global_settings
.cursor_style
)
288 /* Display inverted-line-style */
289 style
= STYLE_INVERT
;
291 #ifdef HAVE_LCD_COLOR
292 else if (global_settings
.cursor_style
== 2)
294 /* Display colour line selector */
295 style
= STYLE_COLORBAR
;
297 else if (global_settings
.cursor_style
== 3)
299 /* Display gradient line selector */
300 style
= STYLE_GRADIENT
;
302 /* Make the lcd driver know how many lines the gradient should
303 cover and current line number */
304 /* number of selected lines */
305 style
|= NUMLN_PACK(list
->selected_size
);
306 /* current line number, zero based */
307 style
|= CURLN_PACK(cur_line
);
311 /* if the text is smaller than the viewport size */
312 if (item_offset
> item_width
- (list_text_vp
->width
- text_pos
))
315 display
->puts_style_xyoffset(0, line
, entry_name
,
316 style
, item_offset
, draw_offset
);
320 display
->puts_scroll_style_xyoffset(0, line
, entry_name
,
321 style
, item_offset
, draw_offset
);
326 if (list
->scroll_all
)
327 display
->puts_scroll_style_xyoffset(0, line
, entry_name
,
328 style
, item_offset
, draw_offset
);
330 display
->puts_style_xyoffset(0, line
, entry_name
,
331 style
, item_offset
, draw_offset
);
334 display
->set_viewport(&list_icons
);
335 if (list
->callback_get_item_icon
!= NULL
)
337 screen_put_icon_with_offset(display
, show_cursor
?1:0,
338 (line
),show_cursor
?ICON_PADDING
:0,draw_offset
,
339 list
->callback_get_item_icon(i
, list
->data
));
341 if (show_cursor
&& i
>= list
->selected_item
&&
342 i
< list
->selected_item
+ list
->selected_size
)
344 screen_put_icon_with_offset(display
, 0, line
, 0, draw_offset
, Icon_Cursor
);
347 display
->set_viewport(parent
);
348 display
->update_viewport();
349 display
->set_viewport(NULL
);
352 #if defined(HAVE_TOUCHSCREEN)
353 /* This needs to be fixed if we ever get more than 1 touchscreen on a target. */
355 static bool released
= false;
357 /* Used for kinetic scrolling as we need to know the last position to
358 * recognize the scroll direction.
359 * This gets reset to 0 at the end of scrolling
361 static int last_position
=0;
363 static int scrollbar_scroll(struct gui_synclist
* gui_list
,
366 const int screen
= screens
[SCREEN_MAIN
].screen_type
;
367 const int nb_lines
= viewport_get_nb_lines(&list_text
[screen
]);
369 if (nb_lines
< gui_list
->nb_items
)
371 /* scrollbar scrolling is still line based */
373 int scrollbar_size
= nb_lines
*
374 font_get(gui_list
->parent
[screen
]->font
)->height
;
375 int actual_y
= y
- list_text
[screen
].y
;
377 int new_selection
= (actual_y
* gui_list
->nb_items
)
380 int start_item
= new_selection
- nb_lines
/2;
383 else if(start_item
> gui_list
->nb_items
- nb_lines
)
384 start_item
= gui_list
->nb_items
- nb_lines
;
386 gui_list
->start_item
[screen
] = start_item
;
388 return ACTION_REDRAW
;
394 /* kinetic scrolling, based on
396 * v = a*t + v0 and ds = v*dt
398 * In each (fixed interval) timeout, the list is advanced by ds, then
399 * the v is reduced by a.
400 * This way we get a linear and smooth deceleration of the scrolling
402 * As v is the difference of distance per time unit, v is passed (as
403 * pixels moved since the last call) to the scrolling function which takes
404 * care of the pixel accurate drawing
406 * v0 is dertermined by averaging the last 4 movements of the list
407 * (the pixel and time difference is used to compute each v)
409 * influenced by http://stechz.com/tag/kinetic/
410 * We take the easy and smooth first approach (until section "Drawbacks"),
411 * since its drawbacks don't apply for us since our timers seem to be
412 * relatively accurate
416 #define SIGN(a) ((a) < 0 ? -1 : 1)
417 /* these could possibly be configurable */
418 /* the lower the smoother */
419 #define RELOAD_INTERVAL (HZ/25)
420 /* the higher the earler the list stops */
421 #define DECELERATION (1000*RELOAD_INTERVAL/HZ)
423 /* this array holds data to compute the initial velocity v0 */
424 static struct kinetic_info
{
428 static size_t cur_idx
;
430 static struct cb_data
{
431 struct gui_synclist
*list
; /* current list */
432 int velocity
; /* in pixel/s */
435 /* data member points to the above struct */
436 static struct timeout kinetic_tmo
;
438 static bool is_kinetic_over(void)
440 return !cb_data
.velocity
&& (scroll_mode
== SCROLL_KINETIC
);
444 * collect data about how fast the list is moved in order to compute
445 * the initial velocity from it later */
446 static void kinetic_stats_collect(const int difference
)
448 static long last_tick
;
449 /* collect velocity statistics */
450 kinetic_data
[cur_idx
].difference
= difference
;
451 kinetic_data
[cur_idx
].ticks
= current_tick
- last_tick
;
453 last_tick
= current_tick
;
455 if (cur_idx
>= ARRAYLEN(kinetic_data
))
456 cur_idx
= 0; /* rewind the index */
460 * resets the statistic */
461 static void kinetic_stats_reset(void)
463 memset(kinetic_data
, 0, sizeof(kinetic_data
));
467 /* cancels all currently active kinetic scrolling */
468 static void kinetic_force_stop(void)
470 timeout_cancel(&kinetic_tmo
);
471 kinetic_stats_reset();
474 /* helper for gui/list.c to cancel scrolling if a normal button event comes
475 * through dpad or keyboard or whatever */
476 void _gui_synclist_stop_kinetic_scrolling(void)
479 if (scroll_mode
== SCROLL_KINETIC
)
480 kinetic_force_stop();
481 scroll_mode
= SCROLL_NONE
;
484 * returns false if scrolling should be stopped entirely
486 * otherwise it returns true even if it didn't actually scroll,
487 * but scrolling mode shouldn't be changed
491 static int scroll_begin_threshold
;
492 static int threshold_accumulation
;
493 static bool swipe_scroll(struct gui_synclist
* gui_list
, int line_height
, int difference
)
496 const enum screen_type screen
= screens
[SCREEN_MAIN
].screen_type
;
497 const int nb_lines
= viewport_get_nb_lines(&list_text
[screen
]);
499 if (UNLIKELY(scroll_begin_threshold
== 0))
500 scroll_begin_threshold
= touchscreen_get_scroll_threshold();
502 /* make selecting items easier */
503 threshold_accumulation
+= abs(difference
);
504 if (threshold_accumulation
< scroll_begin_threshold
&& scroll_mode
== SCROLL_NONE
)
507 threshold_accumulation
= 0;
509 /* does the list even scroll? if no, return but still show
510 * the caller that we would scroll */
511 if (nb_lines
>= gui_list
->nb_items
)
514 const int old_start
= gui_list
->start_item
[screen
];
515 int new_start_item
= -1;
518 /* don't scroll at the edges of the list */
519 if ((old_start
== 0 && difference
> 0)
520 || (old_start
== (gui_list
->nb_items
- nb_lines
) && difference
< 0))
523 gui_list
->start_item
[screen
] = old_start
;
524 return scroll_mode
!= SCROLL_KINETIC
; /* stop kinetic at the edges */
527 /* add up y_offset over time and translate to lines
528 * if scrolled enough */
529 y_offset
+= difference
;
530 if (abs(y_offset
) > line_height
)
532 line_diff
= y_offset
/line_height
;
533 y_offset
-= line_diff
* line_height
;
538 int selection_offset
= gui_list
->selected_item
- old_start
;
539 new_start_item
= old_start
- line_diff
;
540 /* check if new_start_item is bigger than list item count */
541 if(new_start_item
> gui_list
->nb_items
- nb_lines
)
542 new_start_item
= gui_list
->nb_items
- nb_lines
;
543 /* set new_start_item to 0 if it's negative */
544 if(new_start_item
< 0)
547 gui_list
->start_item
[screen
] = new_start_item
;
548 /* keep selected item in sync */
549 gui_list
->selected_item
= new_start_item
+ selection_offset
;
555 static int kinetic_callback(struct timeout
*tmo
)
557 /* cancel if screen was pressed */
558 if (scroll_mode
!= SCROLL_KINETIC
)
561 struct cb_data
*data
= (struct cb_data
*)tmo
->data
;
562 int line_height
= font_get(data
->list
->parent
[0]->font
)->height
;
564 int pixel_diff
= data
->velocity
* RELOAD_INTERVAL
/ HZ
;
565 /* remember signedness to detect stopping */
566 int old_sign
= SIGN(data
->velocity
);
567 /* advance the list */
568 if (!swipe_scroll(data
->list
, line_height
, pixel_diff
))
570 /* nothing to scroll? */
575 /* decelerate by a fixed amount
576 * decrementing v0 over time by the deceleration is
577 * equivalent to computing v = a*t + v0 */
578 data
->velocity
-= SIGN(data
->velocity
)*DECELERATION
;
579 if (SIGN(data
->velocity
) != old_sign
)
583 queue_post(&button_queue
, BUTTON_TOUCHSCREEN
, 0);
584 /* stop if the velocity hit or crossed zero */
587 kinetic_stats_reset();
590 /* let get_action() timeout, which loads to a
591 * gui_synclist_draw() call from the main thread */
592 return RELOAD_INTERVAL
; /* cancel or reload */
596 * computes the initial velocity v0 and sets up the timer */
597 static bool kinetic_setup_scroll(struct gui_synclist
*list
)
599 /* compute initial velocity */
600 int i
, _i
, v0
, len
= ARRAYLEN(kinetic_data
);
601 for(i
= 0, _i
= 0, v0
= 0; i
< len
; i
++)
603 if (kinetic_data
[i
].ticks
> 0)
605 v0
+= kinetic_data
[i
].difference
*HZ
/kinetic_data
[i
].ticks
;
617 cb_data
.velocity
= v0
;
618 timeout_register(&kinetic_tmo
, kinetic_callback
, RELOAD_INTERVAL
, (intptr_t)&cb_data
);
624 unsigned gui_synclist_do_touchscreen(struct gui_synclist
* gui_list
)
627 const enum screen_type screen
= SCREEN_MAIN
;
628 struct viewport
*info_vp
= sb_skin_get_info_vp(screen
);
629 const int button
= action_get_touchscreen_press_in_vp(&x
, &y
, info_vp
);
630 const int list_start_item
= gui_list
->start_item
[screen
];
631 const int line_height
= font_get(gui_list
->parent
[screen
]->font
)->height
;
632 const struct viewport
*list_text_vp
= &list_text
[screen
];
633 const bool old_released
= released
;
634 const bool show_title
= list_display_title(gui_list
, screen
);
635 const bool show_cursor
= !global_settings
.cursor_style
&&
636 gui_list
->show_selection_marker
;
637 const bool on_title_clicked
= show_title
&& y
< line_height
&& (button
&BUTTON_REL
);
638 const bool cancelled_kinetic
= (scroll_mode
== SCROLL_KINETIC
639 && button
!= ACTION_NONE
&& button
!= ACTION_UNKNOWN
640 && !is_kinetic_over());
642 int line
, list_width
= list_text_vp
->width
;
644 released
= (button
&BUTTON_REL
) != 0;
646 if (button
== ACTION_NONE
|| button
== ACTION_UNKNOWN
)
648 /* this happens when we hit edges of the list while kinetic scrolling,
649 * but not when manually cancelling */
650 if (scroll_mode
== SCROLL_KINETIC
)
651 return ACTION_REDRAW
;
655 /* x and y are relative to info_vp */
656 if (gui_list
->callback_get_item_icon
!= NULL
)
657 icon_width
+= get_icon_width(screen
);
659 icon_width
+= get_icon_width(screen
);
661 if (on_title_clicked
)
663 if (scroll_mode
== SCROLL_NONE
|| is_kinetic_over())
667 /* Top left corner is GO_TO_ROOT */
668 if (button
== BUTTON_REL
)
669 return ACTION_STD_MENU
;
670 else if (button
== (BUTTON_REPEAT
|BUTTON_REL
))
671 return ACTION_STD_CONTEXT
;
674 else /* click on title text is cancel */
675 if (button
== BUTTON_REL
)
676 return ACTION_STD_CANCEL
;
678 /* do this after the above so the scrolling stops without
679 * going back in the list with the same touch */
680 if (scroll_mode
== SCROLL_KINETIC
)
682 kinetic_force_stop();
683 scroll_mode
= SCROLL_NONE
;
686 else /* list area clicked (or not released) */
688 const int actual_y
= y
- (show_title
? line_height
: 0);
689 bool on_scrollbar_clicked
;
690 switch (global_settings
.scrollbar
)
693 on_scrollbar_clicked
= x
<= SCROLLBAR_WIDTH
; break;
694 case SCROLLBAR_RIGHT
:
695 on_scrollbar_clicked
= x
> (icon_width
+ list_width
); break;
697 on_scrollbar_clicked
= false; break;
699 /* conditions for scrollbar scrolling:
700 * * pen is on the scrollbar
701 * AND scrollbar is on the right (left case is handled above)
702 * OR * pen is in the somewhere else but we did scrollbar scrolling before
704 * scrollbar scrolling must end if the pen is released
705 * scrollbar scrolling must not happen if we're currently scrolling
706 * via swiping the screen
709 if (!released
&& scroll_mode
!= SCROLL_SWIPE
&&
710 (on_scrollbar_clicked
|| scroll_mode
== SCROLL_BAR
))
712 if (scroll_mode
== SCROLL_KINETIC
)
713 kinetic_force_stop();
714 scroll_mode
= SCROLL_BAR
;
715 return scrollbar_scroll(gui_list
, y
);
718 /* |--------------------------------------------------------|
719 * | Description of the touchscreen list interface: |
720 * |--------------------------------------------------------|
721 * | Pressing an item will select it and "enter" it. |
723 * | Pressing and holding your pen down will scroll through |
724 * | the list of items. |
726 * | Pressing and holding your pen down on a single item |
727 * | will bring up the context menu of it. |
728 * |--------------------------------------------------------|
730 if (actual_y
> 0 || button
& BUTTON_REPEAT
)
732 /* selection needs to be corrected if an items are only
733 * partially visible */
734 line
= (actual_y
- y_offset
) / line_height
;
736 if (cancelled_kinetic
)
738 kinetic_force_stop();
739 scroll_mode
= SCROLL_SWIPE
;
742 /* Pressed below the list*/
743 if (list_start_item
+ line
>= gui_list
->nb_items
)
745 /* don't collect last_position outside of the list area
746 * it'd break selecting after such a situation */
751 if (button
& BUTTON_REPEAT
&& scroll_mode
== SCROLL_NONE
)
753 /* held a single line for a while, bring up the context menu */
754 gui_synclist_select_item(gui_list
, list_start_item
+ line
);
755 /* don't sent context repeatedly */
756 action_wait_for_release();
758 return ACTION_STD_CONTEXT
;
760 if (released
&& !cancelled_kinetic
)
762 /* Pen was released anywhere on the screen */
764 if (scroll_mode
== SCROLL_NONE
)
766 /* select current line */
767 gui_synclist_select_item(gui_list
, list_start_item
+ line
);
768 return ACTION_STD_OK
;
773 * -> reset scrolling but do nothing else */
774 if (scroll_mode
== SCROLL_SWIPE
)
776 if (kinetic_setup_scroll(gui_list
))
777 scroll_mode
= SCROLL_KINETIC
;
779 if (scroll_mode
!= SCROLL_KINETIC
)
780 scroll_mode
= SCROLL_NONE
;
785 { /* pen is on the screen */
786 bool redraw
= false, result
= false;
787 /* beginning of list interaction denoted by release in
788 * the previous call */
789 if (old_released
|| is_kinetic_over())
791 scroll_mode
= SCROLL_NONE
;
795 /* select current item; gui_synclist_select_item()
796 * is not called because it has side effects that
797 * disturb kinetic scrolling */
798 gui_list
->selected_item
= list_start_item
+line
;
799 gui_synclist_speak_item(gui_list
);
800 if (last_position
== 0)
801 last_position
= actual_y
;
804 /* record speed data in case we do kinetic scrolling */
805 int diff
= actual_y
- last_position
;
806 kinetic_stats_collect(diff
);
807 result
= swipe_scroll(gui_list
, line_height
, diff
);
810 /* Start scrolling once the pen is moved without
811 * releasing it inbetween */
815 scroll_mode
= SCROLL_SWIPE
;
817 last_position
= actual_y
;
819 return redraw
? ACTION_REDRAW
:ACTION_NONE
;
823 return ACTION_REDRAW
;