1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2 
3 #include "ui_scrollbar.h"
4 
5 #include <stdlib.h> /* abs */
6 #include <pobl/bl_debug.h>
7 #include <pobl/bl_mem.h> /* free */
8 #include <pobl/bl_str.h> /* strdup */
9 
10 #include "ui_sb_view_factory.h"
11 
12 #define HEIGHT_MARGIN(sb) ((sb)->top_margin + (sb)->bottom_margin)
13 #define IS_TOO_SMALL(sb) ((sb)->window.height <= HEIGHT_MARGIN(sb))
14 
15 #ifdef DEBUG
16 #define MAX_BAR_HEIGHT(sb)                                                    \
17   (IS_TOO_SMALL(sb)                                                           \
18        ? 0 & bl_debug_printf(BL_DEBUG_TAG                                     \
19                              " scroll bar is too small , but MAX_BAR_HEIGHT " \
20                              "was refered.\n")                                \
21        : (sb)->window.height - HEIGHT_MARGIN(sb))
22 #else
23 #define MAX_BAR_HEIGHT(sb) ((sb)->window.height - HEIGHT_MARGIN(sb))
24 #endif
25 
26 #if 0
27 #define __DEBUG
28 #endif
29 
30 /*
31  * For ui_window_update()
32  */
33 enum {
34   UPDATE_UPBUTTON = 0x1,
35   UPDATE_DOWNBUTTON = 0x2,
36   UPDATE_BUTTON = UPDATE_UPBUTTON | UPDATE_DOWNBUTTON,
37 
38   UPDATE_SCROLLBAR = 0x4,
39 
40   FGCOLOR_CHANGED = 0x8,
41   BGCOLOR_CHANGED = 0x10,
42 };
43 
44 /* --- static functions --- */
45 
set_redraw_area(ui_scrollbar_t * sb,int y,u_int height)46 static void set_redraw_area(ui_scrollbar_t *sb, int y, /* Should exclude sb->top_margin. */
47                             u_int height               /* Should be over 0. Can be over
48                                                           sb->window.height -
49                                                           sb->bottom_margin. */
50                             ) {
51   if (sb->redraw_height == 0) {
52     sb->redraw_y = y;
53     sb->redraw_height = height;
54   } else {
55     if (y < sb->redraw_y) {
56       sb->redraw_height += (sb->redraw_y - y);
57       sb->redraw_y = y;
58     }
59 
60     if (y + height > sb->redraw_y + sb->redraw_height) {
61       sb->redraw_height = y + height - sb->redraw_y;
62     }
63   }
64 }
65 
66 /*
67  * Don't call directly draw_xxx functions.
68  * Call ui_window_update() instead.
69  */
draw_scrollbar(ui_scrollbar_t * sb)70 static void draw_scrollbar(ui_scrollbar_t *sb) {
71   if (IS_TOO_SMALL(sb)) {
72     ui_window_blank(&sb->window);
73 
74     return;
75   }
76 
77 #ifdef __DEBUG
78   bl_debug_printf(BL_DEBUG_TAG " updating scrollbar from %d height %d\n", sb->bar_top_y,
79                   sb->bar_height);
80 #endif
81 
82   if (sb->view->draw_scrollbar) {
83     (*sb->view->draw_scrollbar)(sb->view, sb->top_margin + sb->bar_top_y, sb->bar_height);
84   }
85 }
86 
87 /*
88  * Don't call directly draw_xxx functions.
89  * Call ui_window_update() instead.
90  */
draw_background(ui_scrollbar_t * sb)91 static void draw_background(ui_scrollbar_t *sb) {
92   if (IS_TOO_SMALL(sb)) {
93     return;
94   }
95 
96   if (sb->view->draw_background && sb->redraw_height > 0) {
97     int y;
98     int height;
99 
100     /* Redraw upward area of bar. */
101     if (sb->redraw_y < sb->bar_top_y) {
102       y = sb->redraw_y;
103 
104       if (y + sb->redraw_height > sb->bar_top_y) {
105         /* Redraw except bar area. */
106         height = sb->bar_top_y - y;
107       } else {
108         height = sb->redraw_height;
109       }
110 
111 #ifdef __DEBUG
112       bl_debug_printf(BL_DEBUG_TAG " updating background from %d height %d\n", y + sb->top_margin,
113                       height);
114 #endif
115       (*sb->view->draw_background)(sb->view, y + sb->top_margin, height);
116     }
117 
118     /* Redraw downward area of bar. */
119     if (sb->redraw_y < sb->window.height - HEIGHT_MARGIN(sb) &&
120         sb->bar_top_y + sb->bar_height < sb->redraw_y + sb->redraw_height) {
121       if (sb->redraw_y < sb->bar_top_y + sb->bar_height) {
122         y = sb->bar_top_y + sb->bar_height;
123       } else {
124         y = sb->redraw_y;
125       }
126 
127       if (sb->redraw_y + sb->redraw_height > sb->window.height - HEIGHT_MARGIN(sb)) {
128         /* Redraw except bar area. */
129         height = sb->window.height - HEIGHT_MARGIN(sb) - y;
130       } else {
131         height = sb->redraw_y + sb->redraw_height - y;
132       }
133 
134 #ifdef __DEBUG
135       bl_debug_printf(BL_DEBUG_TAG " updating background from %d height %d\n", y + sb->top_margin,
136                       height);
137 #endif
138       (*sb->view->draw_background)(sb->view, y + sb->top_margin, height);
139     }
140 
141     sb->redraw_y = 0;
142     sb->redraw_height = 0;
143   }
144 }
145 
146 /*
147  * Don't call directly draw_xxx functions.
148  * Call ui_window_update() instead.
149  */
draw_button(ui_scrollbar_t * sb,int upbutton,int downbutton)150 static void draw_button(ui_scrollbar_t *sb, int upbutton, int downbutton) {
151   if (IS_TOO_SMALL(sb)) {
152     return;
153   }
154 
155 #ifdef __DEBUG
156   bl_debug_printf(BL_DEBUG_TAG " drawing button.\n");
157 #endif
158 
159   if (upbutton && sb->view->draw_up_button) {
160     (*sb->view->draw_up_button)(sb->view, sb->is_pressing_up_button);
161   }
162 
163   if (downbutton && sb->view->draw_down_button) {
164     (*sb->view->draw_down_button)(sb->view, sb->is_pressing_down_button);
165   }
166 }
167 
168 /*
169  * depends on sb->bar_height.
170  */
calculate_bar_top_y(ui_scrollbar_t * sb)171 static int calculate_bar_top_y(ui_scrollbar_t *sb) {
172   if (IS_TOO_SMALL(sb) || MAX_BAR_HEIGHT(sb) == sb->bar_height ||
173       abs(sb->current_row) == sb->num_filled_log_lines) {
174     return 0;
175   } else {
176     return (sb->current_row + sb->num_filled_log_lines) * (MAX_BAR_HEIGHT(sb) - sb->bar_height) /
177            sb->num_filled_log_lines;
178   }
179 }
180 
181 /*
182  * depends on sb->bar_height.
183  */
calculate_current_row(ui_scrollbar_t * sb)184 static int calculate_current_row(ui_scrollbar_t *sb) {
185   if (IS_TOO_SMALL(sb) || MAX_BAR_HEIGHT(sb) == sb->bar_height) {
186     return 0;
187   } else {
188     /*
189      * sb->bar_top_y / (sb->num_filled_log_lines / (MAX_BAR_HEIGHT(sb) - sb->bar_height))
190      * => (sb->num_filled_log_lines / (MAX_BAR_HEIGHT(sb) - sb->bar_height))
191      *    = pixel per line
192      */
193     return sb->bar_top_y * sb->num_filled_log_lines / (MAX_BAR_HEIGHT(sb) - sb->bar_height) -
194            sb->num_filled_log_lines;
195   }
196 }
197 
calculate_bar_height(ui_scrollbar_t * sb)198 static u_int calculate_bar_height(ui_scrollbar_t *sb) {
199   if (IS_TOO_SMALL(sb) || sb->num_filled_log_lines + sb->num_scr_lines == 0) {
200     return 0;
201   } else {
202     u_int bar_height;
203 
204     bar_height = (sb->num_scr_lines * MAX_BAR_HEIGHT(sb)) /
205                  (sb->num_filled_log_lines + sb->num_scr_lines);
206 
207     if (bar_height < MAX_BAR_HEIGHT(sb) / 20) {
208       bar_height = MAX_BAR_HEIGHT(sb) / 20;
209     }
210 
211     return bar_height;
212   }
213 }
214 
is_updown_button_event(ui_scrollbar_t * sb,int y)215 static int is_updown_button_event(
216     ui_scrollbar_t *sb, int y /* this value must include margin or be y on actual window */
217     ) {
218   int up_button_y;
219   int down_button_y;
220 
221   /*
222    * minus value means y from the bottom.
223    */
224 
225   if (sb->up_button_y < 0) {
226     up_button_y = sb->window.height + sb->up_button_y;
227   } else {
228     up_button_y = sb->up_button_y;
229   }
230 
231   if (sb->down_button_y < 0) {
232     down_button_y = sb->window.height + sb->down_button_y;
233   } else {
234     down_button_y = sb->down_button_y;
235   }
236 
237   if (up_button_y <= y && y <= up_button_y + sb->up_button_height) {
238 #ifdef __DEBUG
239     bl_debug_printf("up button pressed\n");
240 #endif
241 
242     return 1;
243   } else if (down_button_y <= y && y <= down_button_y + sb->down_button_height) {
244 #ifdef __DEBUG
245     bl_debug_printf("down button pressed\n");
246 #endif
247 
248     return -1;
249   } else {
250     return 0;
251   }
252 }
253 
254 /*
255  * callbacks of ui_window_t events.
256  */
257 
trigger_sb_view_realized(ui_scrollbar_t * sb)258 static void trigger_sb_view_realized(ui_scrollbar_t *sb) {
259   if (sb->view->realized) {
260     (*sb->view->realized)(sb->view, sb->window.disp->display, sb->window.disp->screen,
261                           sb->window.my_window, ui_window_get_fg_gc(&sb->window),
262                           sb->window.height);
263   }
264 
265   /*
266    * FGCOLOR_CHANGED|BGCOLOR_CHANGED is necessary in order for
267    * ui_sb_view_t::color_changed to be called. If it is not called,
268    * fg or bg color of buttons are not correctly drawn especially
269    * in changing transparent flag.
270    */
271   ui_window_update(&sb->window, FGCOLOR_CHANGED | BGCOLOR_CHANGED);
272 }
273 
window_realized(ui_window_t * win)274 static void window_realized(ui_window_t *win) {
275   ui_scrollbar_t *sb;
276 
277   sb = (ui_scrollbar_t*)win;
278 
279   if (ui_load_named_xcolor(win->disp, &sb->fg_xcolor, sb->fg_color)) {
280     ui_window_set_fg_color(win, &sb->fg_xcolor);
281   }
282 
283   if (ui_load_named_xcolor(win->disp, &sb->bg_xcolor, sb->bg_color)) {
284     ui_window_set_bg_color(win, &sb->bg_xcolor);
285   }
286 
287   trigger_sb_view_realized(sb);
288 }
289 
window_resized(ui_window_t * win)290 static void window_resized(ui_window_t *win) {
291   ui_scrollbar_t *sb;
292 
293 #ifdef __DEBUG
294   bl_debug_printf(BL_DEBUG_TAG " scrollbar resized.\n");
295 #endif
296 
297   sb = (ui_scrollbar_t*)win;
298 
299   if (IS_TOO_SMALL(sb)) {
300 #ifdef DEBUG
301     bl_warn_printf(BL_DEBUG_TAG " scrollbar is too small to be drawn.\n");
302 #endif
303 
304     sb->num_scr_lines = 0;
305     sb->bar_height = 0;
306     sb->bar_top_y = 0;
307   } else {
308     sb->num_scr_lines = MAX_BAR_HEIGHT(sb) / sb->line_height;
309     sb->bar_height = calculate_bar_height(sb);
310     sb->bar_top_y = MAX_BAR_HEIGHT(sb) - sb->bar_height;
311   }
312   sb->current_row = 0;
313 
314   if (sb->view->resized) {
315     (*sb->view->resized)(sb->view, sb->window.my_window, sb->window.height);
316   }
317 
318   set_redraw_area(sb, 0, sb->window.height);
319   ui_window_update(&sb->window, UPDATE_SCROLLBAR | UPDATE_BUTTON);
320 }
321 
window_exposed(ui_window_t * win,int x,int y,u_int width,u_int height)322 static void window_exposed(ui_window_t *win, int x, int y, u_int width, u_int height) {
323   ui_scrollbar_t *sb;
324 
325   sb = (ui_scrollbar_t*)win;
326 
327   if (y < sb->top_margin) {
328     height -= (sb->top_margin - y);
329     y = 0;
330   } else {
331     y -= sb->top_margin;
332     height -= sb->top_margin;
333   }
334 
335   set_redraw_area(sb, y, height);
336 
337   /*
338    * XXX
339    * GC values should be set(in ui_window_get_gc) before sb->view->func is
340    * called.
341    * For win32: Current gc is set every time window_exposed and update_window.
342    */
343   sb->view->gc = ui_window_get_bg_gc(&sb->window);
344   draw_background(sb);
345 
346   sb->view->gc = ui_window_get_fg_gc(&sb->window);
347   draw_scrollbar(sb);
348   draw_button(sb, 1, 1);
349 }
350 
update_window(ui_window_t * win,int flag)351 static void update_window(ui_window_t *win, int flag) {
352   ui_scrollbar_t *sb;
353 
354   if (flag == 0 || !win->is_mapped /* ui_scrollbar_line_is_added() is called if is_mapped = 0 */) {
355     return;
356   }
357 
358   sb = (ui_scrollbar_t*)win;
359 
360   if (flag & (FGCOLOR_CHANGED | BGCOLOR_CHANGED)) {
361     if (sb->view->color_changed) {
362       if (flag & FGCOLOR_CHANGED) {
363         sb->view->gc = ui_window_get_fg_gc(&sb->window);
364         (*sb->view->color_changed)(sb->view, 1);
365       }
366 
367       if (flag & BGCOLOR_CHANGED) {
368         sb->view->gc = ui_window_get_bg_gc(&sb->window);
369         (*sb->view->color_changed)(sb->view, 0);
370       }
371     }
372   }
373 
374   if (flag & UPDATE_SCROLLBAR) {
375     /*
376      * XXX
377      * GC values should be set(in ui_window_get_gc) before sb->view->func is
378      * called.
379      * For win32: Current gc is set every time window_exposed and update_window.
380      */
381     sb->view->gc = ui_window_get_bg_gc(&sb->window);
382     draw_background(sb);
383 
384     sb->view->gc = ui_window_get_fg_gc(&sb->window);
385     draw_scrollbar(sb);
386   } else {
387     sb->view->gc = ui_window_get_fg_gc(&sb->window);
388   }
389 
390   if (flag & ~UPDATE_SCROLLBAR) {
391     draw_button(sb, (flag & UPDATE_UPBUTTON) != 0, (flag & UPDATE_DOWNBUTTON) != 0);
392   }
393 }
394 
up_button_pressed(ui_scrollbar_t * sb)395 static void up_button_pressed(ui_scrollbar_t *sb) {
396   if (!ui_scrollbar_move_upward(sb, 1)) {
397     return;
398   }
399 
400   if (sb->sb_listener->screen_scroll_downward) {
401     /* up button scrolls *down* screen */
402     (*sb->sb_listener->screen_scroll_downward)(sb->sb_listener->self, 1);
403   }
404 }
405 
down_button_pressed(ui_scrollbar_t * sb)406 static void down_button_pressed(ui_scrollbar_t *sb) {
407   if (!ui_scrollbar_move_downward(sb, 1)) {
408     return;
409   }
410 
411   if (sb->sb_listener->screen_scroll_upward) {
412     /* down button scrolls *up* screen */
413     (*sb->sb_listener->screen_scroll_upward)(sb->sb_listener->self, 1);
414   }
415 }
416 
button_pressed(ui_window_t * win,XButtonEvent * event,int click_num)417 static void button_pressed(ui_window_t *win, XButtonEvent *event, int click_num) {
418   ui_scrollbar_t *sb;
419   int result;
420   int y;
421 
422   sb = (ui_scrollbar_t*)win;
423 
424   if (IS_TOO_SMALL(sb)) {
425     return;
426   }
427 
428   result = is_updown_button_event(sb, event->y);
429 
430   y = event->y - sb->top_margin;
431 
432   if (result == 0) {
433     if (y < sb->bar_top_y) {
434       ui_scrollbar_move_upward(sb, sb->num_scr_lines);
435 
436       if (sb->sb_listener->screen_scroll_downward) {
437         /* down button scrolls *down* screen */
438         (*sb->sb_listener->screen_scroll_downward)(sb->sb_listener->self, sb->num_scr_lines);
439       }
440     } else if (y > sb->bar_top_y + sb->bar_height) {
441       ui_scrollbar_move_downward(sb, sb->num_scr_lines);
442 
443       if (sb->sb_listener->screen_scroll_upward) {
444         /* down button scrolls *up* screen */
445         (*sb->sb_listener->screen_scroll_upward)(sb->sb_listener->self, sb->num_scr_lines);
446       }
447     }
448   } else if (result == 1) {
449     sb->is_pressing_up_button = 1;
450     ui_window_update(&sb->window, UPDATE_UPBUTTON);
451 
452     up_button_pressed(sb);
453   } else if (result == -1) {
454     sb->is_pressing_down_button = 1;
455     ui_window_update(&sb->window, UPDATE_DOWNBUTTON);
456 
457     down_button_pressed(sb);
458   }
459 }
460 
button_press_continued(ui_window_t * win,XButtonEvent * event)461 static void button_press_continued(ui_window_t *win, XButtonEvent *event) {
462   ui_scrollbar_t *sb;
463   int result;
464 
465   sb = (ui_scrollbar_t*)win;
466 
467   result = is_updown_button_event(sb, event->y);
468 
469   if (sb->is_pressing_up_button && result == 1) {
470     up_button_pressed(sb);
471   } else if (sb->is_pressing_down_button && result == -1) {
472     down_button_pressed(sb);
473   }
474 }
475 
button_motion(ui_window_t * win,XMotionEvent * event)476 static void button_motion(ui_window_t *win, XMotionEvent *event) {
477   ui_scrollbar_t *sb;
478   int new_row;
479   int up_to_top_now;
480   int y;
481   int old_bar_top_y;
482   int old_bar_height;
483 
484   sb = (ui_scrollbar_t*)win;
485 
486   if (sb->is_pressing_up_button || sb->is_pressing_down_button ||
487       is_updown_button_event(sb, event->y) != 0 || IS_TOO_SMALL(sb)) {
488     return;
489   }
490 
491   y = event->y - sb->top_margin;
492   old_bar_top_y = sb->bar_top_y;
493   old_bar_height = sb->bar_height;
494 
495   if (sb->bar_top_y == 0) {
496     up_to_top_now = 1;
497   } else {
498     up_to_top_now = 0;
499   }
500 
501   if (sb->is_motion == 0) {
502     if (sb->bar_top_y <= y && y <= sb->bar_top_y + sb->bar_height) {
503       /* on the bar */
504 
505       sb->y_on_bar = y - sb->bar_top_y;
506     } else {
507       /* out of the bar */
508 
509       sb->y_on_bar = sb->bar_height / 2;
510 
511       if (y < sb->y_on_bar) {
512         sb->bar_top_y = 0;
513       } else {
514         sb->bar_top_y = y - sb->y_on_bar;
515       }
516     }
517 
518     sb->is_motion = 1;
519   } else {
520     if (y < sb->y_on_bar) {
521       if (sb->bar_top_y != 0) {
522         sb->bar_top_y = 0;
523       } else {
524         return;
525       }
526     } else if (y - sb->y_on_bar + sb->bar_height > MAX_BAR_HEIGHT(sb)) {
527       sb->bar_top_y = MAX_BAR_HEIGHT(sb) - sb->bar_height;
528     } else {
529       sb->bar_top_y = y - sb->y_on_bar;
530     }
531   }
532 
533   if (!up_to_top_now && sb->bar_top_y == 0) {
534     /* up to the top this time */
535 
536     up_to_top_now = 1;
537   } else {
538     /* if bar is on the top , it is not *this* time(maybe previous...) */
539 
540     up_to_top_now = 0;
541   }
542 
543   new_row = calculate_current_row(sb);
544 
545   /*
546    * if bar reaches the top this time , it doesn't return but draw_scrollbar().
547    */
548   if (!up_to_top_now && sb->current_row == new_row) {
549     /* Restore bar_top_y and bar_height */
550 
551     sb->bar_top_y = old_bar_top_y;
552     sb->bar_height = old_bar_height;
553 
554     return;
555   }
556 
557   sb->current_row = new_row;
558 
559   if (sb->sb_listener->screen_scroll_to) {
560     (*sb->sb_listener->screen_scroll_to)(sb->sb_listener->self, sb->current_row);
561   }
562 
563   set_redraw_area(sb, old_bar_top_y, old_bar_height);
564   ui_window_update(&sb->window, UPDATE_SCROLLBAR);
565 }
566 
button_released(ui_window_t * win,XButtonEvent * event)567 static void button_released(ui_window_t *win, XButtonEvent *event) {
568   ui_scrollbar_t *sb;
569 
570   sb = (ui_scrollbar_t*)win;
571 
572   if (sb->is_pressing_up_button) {
573     sb->is_pressing_up_button = 0;
574     ui_window_update(&sb->window, UPDATE_UPBUTTON);
575   }
576 
577   if (sb->is_pressing_down_button) {
578     sb->is_pressing_down_button = 0;
579     ui_window_update(&sb->window, UPDATE_DOWNBUTTON);
580   }
581 
582   if (sb->is_motion) {
583     sb->is_motion = 0;
584   }
585 }
586 
587 /* --- global functions --- */
588 
ui_scrollbar_init(ui_scrollbar_t * sb,ui_scrollbar_event_listener_t * sb_listener,char * view_name,char * fg_color,char * bg_color,u_int height,u_int line_height,u_int num_log_lines,u_int num_filled_log_lines,int use_transbg,ui_picture_modifier_t * pic_mod)589 int ui_scrollbar_init(ui_scrollbar_t *sb, ui_scrollbar_event_listener_t *sb_listener,
590                       char *view_name, char *fg_color, char *bg_color, u_int height,
591                       u_int line_height, u_int num_log_lines, u_int num_filled_log_lines,
592                       int use_transbg, ui_picture_modifier_t *pic_mod) {
593   u_int width;
594 
595   /* dynamically allocated */
596   sb->view_name = NULL;
597   sb->view = NULL;
598   sb->fg_color = NULL;
599   sb->bg_color = NULL;
600 
601   if (view_name) {
602     sb->view_name = strdup(view_name);
603   } else {
604     sb->view_name = strdup("simple");
605   }
606 
607   if (sb->view_name == NULL) {
608     goto error;
609   }
610 
611   if (use_transbg) {
612     if ((sb->view = ui_transparent_sb_view_new(sb->view_name))) {
613       goto view_created;
614     }
615   }
616 
617   if ((sb->view = ui_sb_view_new(sb->view_name)) == NULL) {
618     free(sb->view_name);
619 
620     if ((sb->view_name = strdup("simple")) == NULL) {
621       goto error;
622     }
623 
624     if (use_transbg) {
625       if ((sb->view = ui_transparent_sb_view_new(sb->view_name))) {
626         goto view_created;
627       }
628     }
629 
630     if ((sb->view = ui_sb_view_new(sb->view_name)) == NULL) {
631       goto error;
632     }
633   }
634 
635   use_transbg = 0;
636 
637 view_created:
638   sb->view->win = &sb->window;
639 
640   sb->sb_listener = sb_listener;
641 
642   (*sb->view->get_geometry_hints)(sb->view, &width, &sb->top_margin, &sb->bottom_margin,
643                                   &sb->up_button_y, &sb->up_button_height, &sb->down_button_y,
644                                   &sb->down_button_height);
645 
646   if (sb->view->get_default_color) {
647     char* _fg_color;
648     char* _bg_color;
649 
650     (*sb->view->get_default_color)(sb->view, &_fg_color, &_bg_color);
651 
652     if (fg_color == NULL) {
653       fg_color = _fg_color;
654     }
655 
656     if (bg_color == NULL) {
657       bg_color = _bg_color;
658     }
659   } else {
660     if (fg_color == NULL) {
661       fg_color = "black";
662     }
663 
664     if (bg_color == NULL) {
665       bg_color = "white";
666     }
667   }
668 
669   sb->fg_color = strdup(fg_color);
670   sb->bg_color = strdup(bg_color);
671 
672   sb->is_pressing_up_button = 0;
673   sb->is_pressing_down_button = 0;
674 
675   if (!ui_window_init(&sb->window, width, height, width, 0, 0, 0, 0, 0, 0, 0)) {
676     goto error;
677   }
678 
679   sb->line_height = line_height;
680 
681   if (IS_TOO_SMALL(sb)) {
682 #ifdef DEBUG
683     bl_warn_printf(BL_DEBUG_TAG " scrollbar is too small to be drawn.\n");
684 #endif
685 
686     sb->bar_height = 0;
687     sb->num_scr_lines = 0;
688   } else {
689     sb->bar_height = height - HEIGHT_MARGIN(sb);
690     sb->num_scr_lines = sb->bar_height / sb->line_height;
691   }
692 
693   sb->num_log_lines = num_log_lines;
694   sb->num_filled_log_lines = num_filled_log_lines;
695   sb->bar_top_y = 0;
696   sb->y_on_bar = 0;
697   sb->current_row = 0;
698   sb->redraw_y = 0;
699   sb->redraw_height = 0;
700   sb->is_motion = 0;
701 
702   if (use_transbg) {
703     ui_window_set_transparent(&sb->window, pic_mod);
704   }
705 
706   ui_window_set_cursor(&sb->window, XC_left_ptr);
707 
708   /*
709    * event callbacks.
710    */
711   ui_window_add_event_mask(&sb->window, ButtonPressMask | ButtonReleaseMask | ButtonMotionMask);
712   sb->window.window_realized = window_realized;
713   sb->window.button_pressed = button_pressed;
714   sb->window.button_released = button_released;
715   sb->window.button_press_continued = button_press_continued;
716   sb->window.button_motion = button_motion;
717   sb->window.window_resized = window_resized;
718   sb->window.window_exposed = window_exposed;
719   sb->window.update_window = update_window;
720 
721   return 1;
722 
723 error:
724   free(sb->fg_color);
725   free(sb->bg_color);
726   free(sb->view_name);
727 
728   if (sb->view) {
729     (*sb->view->destroy)(sb->view);
730   }
731 
732   return 0;
733 }
734 
ui_scrollbar_final(ui_scrollbar_t * sb)735 void ui_scrollbar_final(ui_scrollbar_t *sb) {
736   (*sb->view->destroy)(sb->view);
737   ui_unload_scrollbar_view_lib(sb->view_name);
738 
739   ui_unload_xcolor(sb->window.disp, &sb->fg_xcolor);
740   ui_unload_xcolor(sb->window.disp, &sb->bg_xcolor);
741   free(sb->fg_color);
742   free(sb->bg_color);
743   free(sb->view_name);
744 }
745 
ui_scrollbar_set_num_log_lines(ui_scrollbar_t * sb,u_int num_log_lines)746 void ui_scrollbar_set_num_log_lines(ui_scrollbar_t *sb, u_int num_log_lines) {
747   if (sb->num_log_lines == num_log_lines) {
748     return;
749   }
750 
751   sb->num_log_lines = num_log_lines;
752 
753   if (sb->num_filled_log_lines > sb->num_log_lines) {
754     sb->num_filled_log_lines = sb->num_log_lines;
755   }
756 
757   set_redraw_area(sb, sb->bar_top_y, sb->bar_height);
758 
759   sb->bar_height = calculate_bar_height(sb);
760   sb->bar_top_y = MAX_BAR_HEIGHT(sb) - sb->bar_height;
761 
762   ui_window_update(&sb->window, UPDATE_SCROLLBAR);
763 }
764 
ui_scrollbar_set_num_filled_log_lines(ui_scrollbar_t * sb,u_int lines)765 void ui_scrollbar_set_num_filled_log_lines(ui_scrollbar_t *sb, u_int lines) {
766   if (lines > sb->num_log_lines) {
767     lines = sb->num_log_lines;
768   }
769 
770   if (sb->num_filled_log_lines == lines) {
771     return;
772   }
773 
774   sb->num_filled_log_lines = lines;
775 
776   set_redraw_area(sb, sb->bar_top_y, sb->bar_height);
777 
778   sb->bar_height = calculate_bar_height(sb);
779   sb->bar_top_y = MAX_BAR_HEIGHT(sb) - sb->bar_height;
780 
781   ui_window_update(&sb->window, UPDATE_SCROLLBAR);
782 }
783 
ui_scrollbar_line_is_added(ui_scrollbar_t * sb)784 int ui_scrollbar_line_is_added(ui_scrollbar_t *sb) {
785   int old_bar_top_y;
786   u_int old_bar_height;
787 
788   if ((*sb->sb_listener->screen_is_static)(sb->sb_listener->self)) {
789     if (sb->num_filled_log_lines < sb->num_log_lines) {
790       sb->num_filled_log_lines++;
791     }
792 
793     sb->current_row--;
794   } else if (sb->num_filled_log_lines == sb->num_log_lines) {
795     return 0;
796   } else {
797     sb->num_filled_log_lines++;
798   }
799 
800   old_bar_height = sb->bar_height;
801   sb->bar_height = calculate_bar_height(sb);
802 
803   old_bar_top_y = sb->bar_top_y;
804   sb->bar_top_y = calculate_bar_top_y(sb);
805 
806   if (old_bar_top_y == sb->bar_top_y && old_bar_height == sb->bar_height) {
807     return 1;
808   } else {
809     set_redraw_area(sb, old_bar_top_y, old_bar_height);
810     ui_window_update(&sb->window, UPDATE_SCROLLBAR);
811 
812     return 1;
813   }
814 }
815 
ui_scrollbar_reset(ui_scrollbar_t * sb)816 void ui_scrollbar_reset(ui_scrollbar_t *sb) {
817   if (sb->is_motion || sb->bar_top_y + sb->bar_height < MAX_BAR_HEIGHT(sb)) {
818     set_redraw_area(sb, sb->bar_top_y, sb->bar_height);
819 
820     sb->bar_top_y = MAX_BAR_HEIGHT(sb) - sb->bar_height;
821     sb->is_motion = 0;
822     sb->current_row = 0;
823 
824     ui_window_update(&sb->window, UPDATE_SCROLLBAR);
825   }
826 }
827 
ui_scrollbar_move_upward(ui_scrollbar_t * sb,u_int size)828 int ui_scrollbar_move_upward(ui_scrollbar_t *sb, u_int size) {
829 #if 0
830   if (sb->bar_top_y == 0)
831 #else
832   /*
833    * XXX Adhoc solution
834    * Fix ui_screen.c:bs_{half_}page_{up|down}ward() instead.
835    */
836   if (sb->current_row + sb->num_filled_log_lines == 0)
837 #endif
838   {
839     return 0;
840   }
841 
842   return ui_scrollbar_move(sb, sb->current_row - size);
843 }
844 
ui_scrollbar_move_downward(ui_scrollbar_t * sb,u_int size)845 int ui_scrollbar_move_downward(ui_scrollbar_t *sb, u_int size) {
846   if (sb->current_row >= 0) {
847     return 0;
848   }
849 
850   return ui_scrollbar_move(sb, sb->current_row + size);
851 }
852 
ui_scrollbar_move(ui_scrollbar_t * sb,int row)853 int ui_scrollbar_move(ui_scrollbar_t *sb, int row) {
854   if (0 < row) {
855     row = 0;
856   } else if (row + (int)sb->num_filled_log_lines < 0) {
857     row = -(sb->num_filled_log_lines);
858   }
859 
860   if (sb->current_row == row) {
861     return 0;
862   }
863 
864   sb->current_row = row;
865 
866   set_redraw_area(sb, sb->bar_top_y, sb->bar_height);
867 
868   sb->bar_top_y = calculate_bar_top_y(sb);
869 
870   ui_window_update(&sb->window, UPDATE_SCROLLBAR);
871 
872   return 1;
873 }
874 
ui_scrollbar_set_line_height(ui_scrollbar_t * sb,u_int line_height)875 int ui_scrollbar_set_line_height(ui_scrollbar_t *sb, u_int line_height) {
876   if (sb->line_height == line_height) {
877     return 0;
878   }
879 
880   sb->line_height = line_height;
881 
882   set_redraw_area(sb, sb->bar_top_y, sb->bar_height);
883 
884   sb->bar_height = calculate_bar_height(sb);
885 
886   ui_window_update(&sb->window, UPDATE_SCROLLBAR);
887 
888   return 1;
889 }
890 
ui_scrollbar_set_fg_color(ui_scrollbar_t * sb,char * fg_color)891 int ui_scrollbar_set_fg_color(ui_scrollbar_t *sb, char *fg_color) {
892   free(sb->fg_color);
893   ui_unload_xcolor(sb->window.disp, &sb->fg_xcolor);
894 
895   sb->fg_color = strdup(fg_color);
896 
897   if (ui_load_named_xcolor(sb->window.disp, &sb->fg_xcolor, sb->fg_color)) {
898     ui_window_set_fg_color(&sb->window, &sb->fg_xcolor);
899 
900     set_redraw_area(sb, 0, sb->window.height);
901     ui_window_update(&sb->window, UPDATE_SCROLLBAR | UPDATE_BUTTON | FGCOLOR_CHANGED);
902   }
903 
904   return 1;
905 }
906 
ui_scrollbar_set_bg_color(ui_scrollbar_t * sb,char * bg_color)907 int ui_scrollbar_set_bg_color(ui_scrollbar_t *sb, char *bg_color) {
908   free(sb->bg_color);
909   ui_unload_xcolor(sb->window.disp, &sb->bg_xcolor);
910 
911   sb->bg_color = strdup(bg_color);
912 
913   if (ui_load_named_xcolor(sb->window.disp, &sb->bg_xcolor, sb->bg_color)) {
914     ui_window_set_bg_color(&sb->window, &sb->bg_xcolor);
915 
916     set_redraw_area(sb, 0, sb->window.height);
917     ui_window_update(&sb->window, UPDATE_SCROLLBAR | UPDATE_BUTTON | BGCOLOR_CHANGED);
918   }
919 
920   return 1;
921 }
922 
ui_scrollbar_change_view(ui_scrollbar_t * sb,char * name)923 int ui_scrollbar_change_view(ui_scrollbar_t *sb, char *name) {
924   ui_sb_view_t *view;
925   u_int width;
926 
927   if (strcmp(name, sb->view_name) == 0 || (name = strdup(name)) == NULL) {
928     return 0;
929   }
930 
931   if (sb->window.is_transparent) {
932     if ((view = ui_transparent_sb_view_new(name)) == NULL) {
933       /* nothing is done */
934 
935       free(name);
936 
937       return 0;
938     }
939   } else {
940     if ((view = ui_sb_view_new(name)) == NULL) {
941       free(name);
942 
943       return 0;
944     }
945   }
946 
947   view->win = &sb->window;
948 
949   if (sb->view) {
950     (*sb->view->destroy)(sb->view);
951     ui_unload_scrollbar_view_lib(sb->view_name);
952   }
953 
954   sb->view = view;
955 
956   free(sb->view_name);
957 
958   /* name is dynamically allocated above */
959   sb->view_name = name;
960 
961   (*sb->view->get_geometry_hints)(sb->view, &width, &sb->top_margin, &sb->bottom_margin,
962                                   &sb->up_button_y, &sb->up_button_height, &sb->down_button_y,
963                                   &sb->down_button_height);
964 
965   sb->bar_height = calculate_bar_height(sb);
966   sb->bar_top_y = calculate_bar_top_y(sb);
967 
968   trigger_sb_view_realized(sb);
969 
970   if (sb->window.width != width) {
971     ui_window_set_normal_hints(&sb->window, width, sb->window.min_height, 0, 0);
972 
973     ui_window_resize(&sb->window, width, sb->window.height, NOTIFY_TO_PARENT);
974   }
975 
976   set_redraw_area(sb, 0, sb->window.height);
977 
978   ui_window_update(&sb->window, UPDATE_SCROLLBAR | UPDATE_BUTTON);
979 
980   return 1;
981 }
982 
ui_scrollbar_set_transparent(ui_scrollbar_t * sb,ui_picture_modifier_t * pic_mod,int force)983 int ui_scrollbar_set_transparent(ui_scrollbar_t *sb, ui_picture_modifier_t *pic_mod, int force) {
984   ui_sb_view_t *view;
985 
986   if (!force && sb->window.is_transparent) {
987     /* already set */
988 
989     return 1;
990   }
991 
992   if ((view = ui_transparent_sb_view_new(sb->view_name)) == NULL) {
993     /* nothing is done */
994 
995     return 0;
996   }
997 
998   view->win = &sb->window;
999 
1000   if (sb->view) {
1001     (*sb->view->destroy)(sb->view);
1002   }
1003 
1004   sb->view = view;
1005 
1006   /* This should be done before ui_window_set_untransparent() , which calls
1007    * exposed event. */
1008   trigger_sb_view_realized(sb);
1009 
1010   ui_window_set_transparent(&sb->window, pic_mod);
1011 
1012   set_redraw_area(sb, 0, sb->window.height);
1013   ui_window_update(&sb->window, UPDATE_SCROLLBAR | UPDATE_BUTTON);
1014 
1015   return 1;
1016 }
1017 
ui_scrollbar_unset_transparent(ui_scrollbar_t * sb)1018 int ui_scrollbar_unset_transparent(ui_scrollbar_t *sb) {
1019   ui_sb_view_t *view;
1020 
1021   if (!sb->window.is_transparent) {
1022     /* already unset */
1023 
1024     return 1;
1025   }
1026 
1027   if ((view = ui_sb_view_new(sb->view_name)) == NULL) {
1028     /* nothing is done */
1029 
1030     return 0;
1031   }
1032 
1033   view->win = &sb->window;
1034 
1035   if (sb->view) {
1036     (*sb->view->destroy)(sb->view);
1037   }
1038 
1039   sb->view = view;
1040 
1041   /* This should be done before ui_window_set_untransparent() , which calls
1042    * exposed event. */
1043   trigger_sb_view_realized(sb);
1044 
1045   ui_window_unset_transparent(&sb->window);
1046 
1047   set_redraw_area(sb, 0, sb->window.height);
1048   ui_window_update(&sb->window, UPDATE_SCROLLBAR | UPDATE_BUTTON);
1049 
1050   return 1;
1051 }
1052