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
= list_scaled_line_height(title_text_vp
);
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_with_offset_h(display
, 0, 0, 0, 0, 50, 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 lcd_puts_scroll_style_h(0, 0, list
->title
, style
, 50);
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
= list_scaled_line_height(parent
);
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 int title_height
= title_text
[screen
].height
;
163 list_text_vp
->y
+= title_height
;
164 list_text_vp
->height
-= title_height
;
167 const int nb_lines
= list_max_scaled_lines_in_vp(list_text_vp
);
169 start
= list_start_item
;
170 end
= start
+ nb_lines
;
172 #ifdef HAVE_TOUCHSCREEN
173 if (list
->selected_item
== 0 || (list
->nb_items
< nb_lines
))
174 y_offset
= 0; /* reset in case it's a new list */
176 int draw_offset
= y_offset
;
177 /* draw some extra items to not have empty lines at the top and bottom */
180 /* make it negative for more consistent apparence when switching
182 draw_offset
-= line_height
;
186 else if (y_offset
< 0)
189 #define draw_offset 0
192 /* draw the scrollbar if its needed */
193 if (global_settings
.scrollbar
&& nb_lines
< list
->nb_items
)
195 struct viewport vp
= *list_text_vp
;
196 vp
.width
= SCROLLBAR_WIDTH
;
197 vp
.height
= line_height
* nb_lines
;
199 list_text_vp
->width
-= SCROLLBAR_WIDTH
;
200 if (scrollbar_in_left
)
201 list_text_vp
->x
+= SCROLLBAR_WIDTH
;
203 vp
.x
+= list_text_vp
->width
;
204 display
->set_viewport(&vp
);
205 gui_scrollbar_draw(display
,
206 (scrollbar_in_left
? 0: 1), 0, SCROLLBAR_WIDTH
-1, vp
.height
,
207 list
->nb_items
, list_start_item
, list_start_item
+ nb_lines
,
212 /* shift everything a bit in relation to the title... */
213 if (!VP_IS_RTL(list_text_vp
) && scrollbar_in_left
)
215 list_text_vp
->width
-= SCROLLBAR_WIDTH
;
216 list_text_vp
->x
+= SCROLLBAR_WIDTH
;
218 else if (VP_IS_RTL(list_text_vp
) && !scrollbar_in_left
)
220 list_text_vp
->width
-= SCROLLBAR_WIDTH
;
224 /* setup icon placement */
225 list_icons
= *list_text_vp
;
226 int icon_count
= (list
->callback_get_item_icon
!= NULL
) ? 1 : 0;
231 list_icons
.width
= icon_width
* icon_count
;
232 list_text_vp
->width
-= list_icons
.width
+ ICON_PADDING
;
233 if (VP_IS_RTL(&list_icons
))
234 list_icons
.x
+= list_text_vp
->width
+ ICON_PADDING
;
236 list_text_vp
->x
+= list_icons
.width
+ ICON_PADDING
;
239 for (i
=start
; i
<end
&& i
<list
->nb_items
; i
++)
242 unsigned const char *s
;
243 char entry_buffer
[MAX_PATH
];
244 unsigned char *entry_name
;
246 int line
= i
- start
;
247 s
= list
->callback_get_item_name(i
, list
->data
, entry_buffer
,
248 sizeof(entry_buffer
));
249 entry_name
= P2STR(s
);
250 display
->set_viewport(list_text_vp
);
251 style
= STYLE_DEFAULT
;
252 /* position the string at the correct offset place */
254 display
->getstringsize(entry_name
, &item_width
, &h
);
255 item_offset
= gui_list_get_item_offset(list
, item_width
, text_pos
,
256 display
, list_text_vp
);
258 #ifdef HAVE_LCD_COLOR
259 /* if the list has a color callback */
260 if (list
->callback_get_item_color
)
262 int color
= list
->callback_get_item_color(i
, list
->data
);
263 /* if color selected */
266 style
|= STYLE_COLORED
|color
;
270 /* draw the selected line */
272 #ifdef HAVE_TOUCHSCREEN
273 /* don't draw it during scrolling */
274 scroll_mode
== SCROLL_NONE
&&
276 i
>= list
->selected_item
277 && i
< list
->selected_item
+ list
->selected_size
278 && list
->show_selection_marker
)
279 {/* The selected item must be displayed scrolling */
280 if (global_settings
.cursor_style
== 1
281 #ifdef HAVE_REMOTE_LCD
282 /* the global_settings.cursor_style check is here to make
283 * sure if they want the cursor instead of bar it will work
285 || (display
->depth
< 16 && global_settings
.cursor_style
)
289 /* Display inverted-line-style */
290 style
= STYLE_INVERT
;
292 #ifdef HAVE_LCD_COLOR
293 else if (global_settings
.cursor_style
== 2)
295 /* Display colour line selector */
296 style
= STYLE_COLORBAR
;
298 else if (global_settings
.cursor_style
== 3)
300 /* Display gradient line selector */
301 style
= STYLE_GRADIENT
;
303 /* Make the lcd driver know how many lines the gradient should
304 cover and current line number */
305 /* number of selected lines */
306 style
|= NUMLN_PACK(list
->selected_size
);
307 /* current line number, zero based */
308 style
|= CURLN_PACK(cur_line
);
312 /* if the text is smaller than the viewport size */
313 if (item_offset
> item_width
- (list_text_vp
->width
- text_pos
))
316 lcd_puts_style_xyoffset_h(0, line
, entry_name
,
317 style
, item_offset
, draw_offset
, line_height
);
321 lcd_puts_scroll_style_xyoffset_h(0, line
, entry_name
,
322 style
, item_offset
, draw_offset
, line_height
);
327 if (list
->scroll_all
)
328 lcd_puts_scroll_style_xyoffset_h(0, line
, entry_name
,
329 style
, item_offset
, draw_offset
, line_height
);
331 lcd_puts_style_xyoffset_h(0, line
, entry_name
,
332 style
, item_offset
, draw_offset
, line_height
);
335 display
->set_viewport(&list_icons
);
336 if (list
->callback_get_item_icon
!= NULL
)
338 screen_put_icon_with_offset_h(display
, show_cursor
?1:0,
339 (line
),show_cursor
?ICON_PADDING
:0,draw_offset
, line_height
,
340 list
->callback_get_item_icon(i
, list
->data
));
342 if (show_cursor
&& i
>= list
->selected_item
&&
343 i
< list
->selected_item
+ list
->selected_size
)
345 screen_put_icon_with_offset_h(display
, 0, line
, 0, draw_offset
, line_height
, Icon_Cursor
);
348 display
->set_viewport(parent
);
349 display
->update_viewport();
350 display
->set_viewport(NULL
);
353 #if defined(HAVE_TOUCHSCREEN)
354 /* This needs to be fixed if we ever get more than 1 touchscreen on a target. */
356 static bool released
= false;
358 /* Used for kinetic scrolling as we need to know the last position to
359 * recognize the scroll direction.
360 * This gets reset to 0 at the end of scrolling
362 static int last_position
=0;
364 static int scrollbar_scroll(struct gui_synclist
* gui_list
,
367 const int screen
= screens
[SCREEN_MAIN
].screen_type
;
368 const int nb_lines
= list_max_scaled_lines_in_vp(&list_text
[screen
]);
370 if (nb_lines
< gui_list
->nb_items
)
372 /* scrollbar scrolling is still line based */
374 int scrollbar_size
= nb_lines
*
375 list_scaled_line_height(gui_list
->parent
[screen
]);
376 int actual_y
= y
- list_text
[screen
].y
;
378 int new_selection
= (actual_y
* gui_list
->nb_items
)
381 int start_item
= new_selection
- nb_lines
/2;
384 else if(start_item
> gui_list
->nb_items
- nb_lines
)
385 start_item
= gui_list
->nb_items
- nb_lines
;
387 gui_list
->start_item
[screen
] = start_item
;
389 return ACTION_REDRAW
;
395 /* kinetic scrolling, based on
397 * v = a*t + v0 and ds = v*dt
399 * In each (fixed interval) timeout, the list is advanced by ds, then
400 * the v is reduced by a.
401 * This way we get a linear and smooth deceleration of the scrolling
403 * As v is the difference of distance per time unit, v is passed (as
404 * pixels moved since the last call) to the scrolling function which takes
405 * care of the pixel accurate drawing
407 * v0 is dertermined by averaging the last 4 movements of the list
408 * (the pixel and time difference is used to compute each v)
410 * influenced by http://stechz.com/tag/kinetic/
411 * We take the easy and smooth first approach (until section "Drawbacks"),
412 * since its drawbacks don't apply for us since our timers seem to be
413 * relatively accurate
417 #define SIGN(a) ((a) < 0 ? -1 : 1)
418 /* these could possibly be configurable */
419 /* the lower the smoother */
420 #define RELOAD_INTERVAL (HZ/25)
421 /* the higher the earler the list stops */
422 #define DECELERATION (1000*RELOAD_INTERVAL/HZ)
424 /* this array holds data to compute the initial velocity v0 */
425 static struct kinetic_info
{
429 static size_t cur_idx
;
431 static struct cb_data
{
432 struct gui_synclist
*list
; /* current list */
433 int velocity
; /* in pixel/s */
436 /* data member points to the above struct */
437 static struct timeout kinetic_tmo
;
439 static bool is_kinetic_over(void)
441 return !cb_data
.velocity
&& (scroll_mode
== SCROLL_KINETIC
);
445 * collect data about how fast the list is moved in order to compute
446 * the initial velocity from it later */
447 static void kinetic_stats_collect(const int difference
)
449 static long last_tick
;
450 /* collect velocity statistics */
451 kinetic_data
[cur_idx
].difference
= difference
;
452 kinetic_data
[cur_idx
].ticks
= current_tick
- last_tick
;
454 last_tick
= current_tick
;
456 if (cur_idx
>= ARRAYLEN(kinetic_data
))
457 cur_idx
= 0; /* rewind the index */
461 * resets the statistic */
462 static void kinetic_stats_reset(void)
464 memset(kinetic_data
, 0, sizeof(kinetic_data
));
468 /* cancels all currently active kinetic scrolling */
469 static void kinetic_force_stop(void)
471 timeout_cancel(&kinetic_tmo
);
472 kinetic_stats_reset();
475 /* helper for gui/list.c to cancel scrolling if a normal button event comes
476 * through dpad or keyboard or whatever */
477 void _gui_synclist_stop_kinetic_scrolling(void)
480 if (scroll_mode
== SCROLL_KINETIC
)
481 kinetic_force_stop();
482 scroll_mode
= SCROLL_NONE
;
485 * returns false if scrolling should be stopped entirely
487 * otherwise it returns true even if it didn't actually scroll,
488 * but scrolling mode shouldn't be changed
492 static int scroll_begin_threshold
;
493 static int threshold_accumulation
;
494 static bool swipe_scroll(struct gui_synclist
* gui_list
, int line_height
, int difference
)
497 const enum screen_type screen
= screens
[SCREEN_MAIN
].screen_type
;
498 const int nb_lines
= list_max_scaled_lines_in_vp(&list_text
[screen
]);
500 if (UNLIKELY(scroll_begin_threshold
== 0))
501 scroll_begin_threshold
= touchscreen_get_scroll_threshold();
503 /* make selecting items easier */
504 threshold_accumulation
+= abs(difference
);
505 if (threshold_accumulation
< scroll_begin_threshold
&& scroll_mode
== SCROLL_NONE
)
508 threshold_accumulation
= 0;
510 /* does the list even scroll? if no, return but still show
511 * the caller that we would scroll */
512 if (nb_lines
>= gui_list
->nb_items
)
515 const int old_start
= gui_list
->start_item
[screen
];
516 int new_start_item
= -1;
519 /* don't scroll at the edges of the list */
520 if ((old_start
== 0 && difference
> 0)
521 || (old_start
== (gui_list
->nb_items
- nb_lines
) && difference
< 0))
524 gui_list
->start_item
[screen
] = old_start
;
525 return scroll_mode
!= SCROLL_KINETIC
; /* stop kinetic at the edges */
528 /* add up y_offset over time and translate to lines
529 * if scrolled enough */
530 y_offset
+= difference
;
531 if (abs(y_offset
) > line_height
)
533 line_diff
= y_offset
/line_height
;
534 y_offset
-= line_diff
* line_height
;
539 int selection_offset
= gui_list
->selected_item
- old_start
;
540 new_start_item
= old_start
- line_diff
;
541 /* check if new_start_item is bigger than list item count */
542 if(new_start_item
> gui_list
->nb_items
- nb_lines
)
543 new_start_item
= gui_list
->nb_items
- nb_lines
;
544 /* set new_start_item to 0 if it's negative */
545 if(new_start_item
< 0)
548 gui_list
->start_item
[screen
] = new_start_item
;
549 /* keep selected item in sync */
550 gui_list
->selected_item
= new_start_item
+ selection_offset
;
556 static int kinetic_callback(struct timeout
*tmo
)
558 /* cancel if screen was pressed */
559 if (scroll_mode
!= SCROLL_KINETIC
)
562 struct cb_data
*data
= (struct cb_data
*)tmo
->data
;
563 int line_height
= list_scaled_line_height(data
->list
->parent
[0]);
565 int pixel_diff
= data
->velocity
* RELOAD_INTERVAL
/ HZ
;
566 /* remember signedness to detect stopping */
567 int old_sign
= SIGN(data
->velocity
);
568 /* advance the list */
569 if (!swipe_scroll(data
->list
, line_height
, pixel_diff
))
571 /* nothing to scroll? */
576 /* decelerate by a fixed amount
577 * decrementing v0 over time by the deceleration is
578 * equivalent to computing v = a*t + v0 */
579 data
->velocity
-= SIGN(data
->velocity
)*DECELERATION
;
580 if (SIGN(data
->velocity
) != old_sign
)
584 queue_post(&button_queue
, BUTTON_TOUCHSCREEN
, 0);
585 /* stop if the velocity hit or crossed zero */
588 kinetic_stats_reset();
591 /* let get_action() timeout, which loads to a
592 * gui_synclist_draw() call from the main thread */
593 return RELOAD_INTERVAL
; /* cancel or reload */
597 * computes the initial velocity v0 and sets up the timer */
598 static bool kinetic_setup_scroll(struct gui_synclist
*list
)
600 /* compute initial velocity */
601 int i
, _i
, v0
, len
= ARRAYLEN(kinetic_data
);
602 for(i
= 0, _i
= 0, v0
= 0; i
< len
; i
++)
604 if (kinetic_data
[i
].ticks
> 0)
606 v0
+= kinetic_data
[i
].difference
*HZ
/kinetic_data
[i
].ticks
;
618 cb_data
.velocity
= v0
;
619 timeout_register(&kinetic_tmo
, kinetic_callback
, RELOAD_INTERVAL
, (intptr_t)&cb_data
);
625 unsigned gui_synclist_do_touchscreen(struct gui_synclist
* gui_list
)
628 const enum screen_type screen
= SCREEN_MAIN
;
629 struct viewport
*info_vp
= sb_skin_get_info_vp(screen
);
630 const int button
= action_get_touchscreen_press_in_vp(&x
, &y
, info_vp
);
631 const int list_start_item
= gui_list
->start_item
[screen
];
632 const int line_height
= list_scaled_line_height(gui_list
->parent
[screen
]);
633 const struct viewport
*list_text_vp
= &list_text
[screen
];
634 const bool old_released
= released
;
635 const bool show_title
= list_display_title(gui_list
, screen
);
636 const bool show_cursor
= !global_settings
.cursor_style
&&
637 gui_list
->show_selection_marker
;
638 const bool on_title_clicked
= show_title
&& y
< line_height
&& (button
&BUTTON_REL
);
639 const bool cancelled_kinetic
= (scroll_mode
== SCROLL_KINETIC
640 && button
!= ACTION_NONE
&& button
!= ACTION_UNKNOWN
641 && !is_kinetic_over());
643 int line
, list_width
= list_text_vp
->width
;
644 static bool wait_for_release
= false;
646 released
= (button
&BUTTON_REL
) != 0;
648 if (released
&& wait_for_release
)
649 { /* was waiting on a release, reset everything so the next call
650 * can start from new */
651 wait_for_release
= false;
653 if (scroll_mode
== SCROLL_KINETIC
)
654 kinetic_force_stop();
655 scroll_mode
= SCROLL_NONE
;
659 if (button
== ACTION_NONE
|| button
== ACTION_UNKNOWN
)
661 /* this happens when we hit edges of the list while kinetic scrolling,
662 * but not when manually cancelling */
663 if (scroll_mode
== SCROLL_KINETIC
)
664 return ACTION_REDRAW
;
668 /* x and y are relative to info_vp */
669 if (gui_list
->callback_get_item_icon
!= NULL
)
670 icon_width
+= get_icon_width(screen
);
672 icon_width
+= get_icon_width(screen
);
674 if (on_title_clicked
)
676 if (scroll_mode
== SCROLL_NONE
|| is_kinetic_over())
680 /* Top left corner is GO_TO_ROOT */
681 if (button
== BUTTON_REL
)
682 return ACTION_STD_MENU
;
683 else if (button
== (BUTTON_REPEAT
|BUTTON_REL
))
684 return ACTION_STD_CONTEXT
;
687 else /* click on title text is cancel */
688 if (button
== BUTTON_REL
)
689 return ACTION_STD_CANCEL
;
691 /* do this after the above so the scrolling stops without
692 * going back in the list with the same touch */
693 if (scroll_mode
== SCROLL_KINETIC
)
695 kinetic_force_stop();
696 scroll_mode
= SCROLL_NONE
;
699 else /* list area clicked (or not released) */
701 const int actual_y
= y
- (show_title
? line_height
: 0);
702 bool on_scrollbar_clicked
;
703 switch (global_settings
.scrollbar
)
706 on_scrollbar_clicked
= x
<= SCROLLBAR_WIDTH
; break;
707 case SCROLLBAR_RIGHT
:
708 on_scrollbar_clicked
= x
> (icon_width
+ list_width
); break;
710 on_scrollbar_clicked
= false; break;
712 /* conditions for scrollbar scrolling:
713 * * pen is on the scrollbar
714 * AND scrollbar is on the right (left case is handled above)
715 * OR * pen is in the somewhere else but we did scrollbar scrolling before
717 * scrollbar scrolling must end if the pen is released
718 * scrollbar scrolling must not happen if we're currently scrolling
719 * via swiping the screen
722 if (!released
&& scroll_mode
!= SCROLL_SWIPE
&&
723 (on_scrollbar_clicked
|| scroll_mode
== SCROLL_BAR
))
725 if (scroll_mode
== SCROLL_KINETIC
)
726 kinetic_force_stop();
727 scroll_mode
= SCROLL_BAR
;
728 return scrollbar_scroll(gui_list
, y
);
731 /* |--------------------------------------------------------|
732 * | Description of the touchscreen list interface: |
733 * |--------------------------------------------------------|
734 * | Pressing an item will select it and "enter" it. |
736 * | Pressing and holding your pen down will scroll through |
737 * | the list of items. |
739 * | Pressing and holding your pen down on a single item |
740 * | will bring up the context menu of it. |
741 * |--------------------------------------------------------|
743 if (actual_y
> 0 || button
& BUTTON_REPEAT
)
745 /* selection needs to be corrected if an items are only
746 * partially visible */
747 line
= (actual_y
- y_offset
) / line_height
;
749 if (cancelled_kinetic
)
751 kinetic_force_stop();
752 scroll_mode
= SCROLL_SWIPE
;
755 /* Pressed below the list*/
756 if (list_start_item
+ line
>= gui_list
->nb_items
)
758 /* don't collect last_position outside of the list area
759 * it'd break selecting after such a situation */
764 if (button
& BUTTON_REPEAT
&& scroll_mode
== SCROLL_NONE
765 && !wait_for_release
)
767 /* held a single line for a while, bring up the context menu */
768 gui_synclist_select_item(gui_list
, list_start_item
+ line
);
769 /* don't sent context repeatedly */
770 wait_for_release
= true;
771 return ACTION_STD_CONTEXT
;
773 if (released
&& !cancelled_kinetic
)
775 /* Pen was released anywhere on the screen */
777 if (scroll_mode
== SCROLL_NONE
)
779 /* select current line */
780 gui_synclist_select_item(gui_list
, list_start_item
+ line
);
781 return ACTION_STD_OK
;
786 * -> reset scrolling but do nothing else */
787 if (scroll_mode
== SCROLL_SWIPE
)
789 if (kinetic_setup_scroll(gui_list
))
790 scroll_mode
= SCROLL_KINETIC
;
792 if (scroll_mode
!= SCROLL_KINETIC
)
793 scroll_mode
= SCROLL_NONE
;
798 { /* pen is on the screen */
799 bool redraw
= false, result
= false;
800 /* beginning of list interaction denoted by release in
801 * the previous call */
802 if (old_released
|| is_kinetic_over())
804 scroll_mode
= SCROLL_NONE
;
808 /* select current item; gui_synclist_select_item()
809 * is not called because it has side effects that
810 * disturb kinetic scrolling */
811 gui_list
->selected_item
= list_start_item
+line
;
812 gui_synclist_speak_item(gui_list
);
813 if (last_position
== 0)
814 last_position
= actual_y
;
817 /* record speed data in case we do kinetic scrolling */
818 int diff
= actual_y
- last_position
;
819 kinetic_stats_collect(diff
);
820 result
= swipe_scroll(gui_list
, line_height
, diff
);
823 /* Start scrolling once the pen is moved without
824 * releasing it inbetween */
828 scroll_mode
= SCROLL_SWIPE
;
830 last_position
= actual_y
;
832 return redraw
? ACTION_REDRAW
:ACTION_NONE
;
836 return ACTION_REDRAW
;