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