1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2 
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <ui_sb_view.h>
6 
7 #include "exsb_common.h"
8 #include "next_data.h"
9 
10 #define WIDTH 18
11 #define BOTTOM_MARGIN 35
12 
13 #define BUTTON_SIZE 16
14 #define UP_BUTTON_Y(view_height) ((view_height)-BUTTON_SIZE * 2 - 2)
15 #define DOWN_BUTTON_Y(view_height) ((view_height)-BUTTON_SIZE - 1)
16 
17 #define BAR_RELIEF_SIZE 6
18 #define BAR_RELIEF_X 5
19 
20 typedef struct next_sb_view {
21   ui_sb_view_t view;
22 
23   GC gc;
24 
25   unsigned int depth;
26 
27   Pixmap background;
28   Pixmap bar_relief;
29   Pixmap arrow_up;
30   Pixmap arrow_up_pressed;
31   Pixmap arrow_down;
32   Pixmap arrow_down_pressed;
33 
34   unsigned long gray_light;
35   unsigned long gray_dark;
36 
37   int has_scrollbuf;
38   int is_transparent;
39 
40 } next_sb_view_t;
41 
42 /* --- static functions --- */
43 
get_icon_pixmap(ui_sb_view_t * view,GC gc,char ** data,unsigned int width,unsigned int height)44 static Pixmap get_icon_pixmap(ui_sb_view_t *view, GC gc, char **data, unsigned int width,
45                               unsigned int height) {
46   Pixmap pix;
47   next_sb_view_t *next_sb;
48   char cur = '\0';
49   short x;
50   short y;
51   XPoint *xpoint;
52   int i = 0;
53 
54   next_sb = (next_sb_view_t *)view;
55 
56   pix = XCreatePixmap(view->display, view->window, width, height, next_sb->depth);
57 
58   if ((xpoint = malloc((width * height) * sizeof(XPoint))) == NULL) {
59     return pix;
60   }
61 
62   for (y = 0; y < height; y++) {
63     for (x = 0; x < width; x++) {
64       if (cur != data[y][x]) {
65         if (i) {
66           /* before setting gc,
67                            draw stocked points */
68           XDrawPoints(view->display, pix, gc, xpoint, i, CoordModeOrigin);
69           i = 0;
70         }
71 
72         /* changing gc */
73         if (data[y][x] == ' ') {
74           XSetForeground(view->display, gc, WhitePixel(view->display, view->screen));
75         } else if (data[y][x] == '#') {
76           XSetForeground(view->display, gc, BlackPixel(view->display, view->screen));
77         } else if (data[y][x] == '+') {
78           XSetForeground(view->display, gc, next_sb->gray_dark);
79         } else if (data[y][x] == '-') {
80           XSetForeground(view->display, gc, next_sb->gray_light);
81         }
82 
83         cur = data[y][x];
84       }
85 
86       /* stocking point */
87       xpoint[i].x = x;
88       xpoint[i].y = y;
89       i++;
90     }
91   }
92 
93   if (i) {
94     XDrawPoints(view->display, pix, gc, xpoint, i, CoordModeOrigin);
95   }
96 
97   free(xpoint);
98 
99   return pix;
100 }
101 
get_geometry_hints(ui_sb_view_t * view,unsigned int * width,unsigned int * top_margin,unsigned int * bottom_margin,int * up_button_y,unsigned int * up_button_height,int * down_button_y,unsigned int * down_button_height)102 static void get_geometry_hints(ui_sb_view_t *view, unsigned int *width, unsigned int *top_margin,
103                                unsigned int *bottom_margin, int *up_button_y,
104                                unsigned int *up_button_height, int *down_button_y,
105                                unsigned int *down_button_height) {
106   *width = WIDTH;
107   *top_margin = 0;
108   *bottom_margin = BOTTOM_MARGIN;
109   *up_button_y = -(BUTTON_SIZE + 1) * 2;
110   *up_button_height = BUTTON_SIZE;
111   *down_button_y = -(BUTTON_SIZE + 1);
112   *down_button_height = BUTTON_SIZE;
113 }
114 
get_default_color(ui_sb_view_t * view,char ** fg_color,char ** bg_color)115 static void get_default_color(ui_sb_view_t *view, char **fg_color, char **bg_color) {
116   *fg_color = "gray";
117   *bg_color = "lightgray";
118 }
119 
create_bg(ui_sb_view_t * view,int width,int height)120 static Pixmap create_bg(ui_sb_view_t *view, int width, int height) {
121   Pixmap pix;
122   next_sb_view_t *next_sb;
123   short x;
124   short y;
125   XPoint *xpoint;
126   int i = 0;
127 
128   next_sb = (next_sb_view_t *)view;
129 
130   pix = XCreatePixmap(view->display, view->window, width, height, ((next_sb_view_t *)view)->depth);
131 
132   XSetForeground(view->display, next_sb->gc, next_sb->gray_light);
133   XFillRectangle(view->display, pix, next_sb->gc, 0, 0, width, height);
134 
135   if ((xpoint = malloc((width * height / 2) * sizeof(XPoint))) == NULL) {
136     return pix;
137   }
138 
139   XSetForeground(view->display, next_sb->gc, next_sb->gray_dark);
140   for (y = 0; y < height; y += 2) {
141     for (x = 1; x < width - 1; x += 2) {
142       xpoint[i].x = x;
143       xpoint[i].y = y;
144       i++;
145     }
146   }
147   for (y = 1; y < height; y += 2) {
148     for (x = 2; x < width - 1; x += 2) {
149       xpoint[i].x = x;
150       xpoint[i].y = y;
151       i++;
152     }
153   }
154   XDrawPoints(view->display, pix, next_sb->gc, xpoint, i, CoordModeOrigin);
155 
156   free(xpoint);
157 
158   return pix;
159 }
160 
realized(ui_sb_view_t * view,Display * display,int screen,Window window,GC gc,unsigned int height)161 static void realized(ui_sb_view_t *view, Display *display, int screen, Window window, GC gc,
162                      unsigned int height) {
163   next_sb_view_t *next_sb;
164   XWindowAttributes attr;
165   XGCValues gc_value;
166 
167   next_sb = (next_sb_view_t *)view;
168 
169   view->display = display;
170   view->screen = screen;
171   view->window = window;
172   view->gc = gc;
173   view->height = height;
174 
175   gc_value.foreground = BlackPixel(view->display, view->screen);
176   gc_value.background = WhitePixel(view->display, view->screen);
177   gc_value.graphics_exposures = 0;
178 
179   next_sb->gc = XCreateGC(view->display, view->window,
180                           GCForeground | GCBackground | GCGraphicsExposures, &gc_value);
181 
182   XGetWindowAttributes(view->display, view->window, &attr);
183   next_sb->depth = attr.depth;
184 
185   next_sb->gray_light =
186       exsb_get_pixel(view->display, view->screen, attr.colormap, attr.visual, "rgb:ae/aa/ae");
187   next_sb->gray_dark =
188       exsb_get_pixel(view->display, view->screen, attr.colormap, attr.visual, "rgb:51/55/51");
189 
190   next_sb->background = create_bg(view, WIDTH, view->height);
191   next_sb->bar_relief =
192       get_icon_pixmap(view, next_sb->gc, bar_relief_src, BAR_RELIEF_SIZE, BAR_RELIEF_SIZE);
193   next_sb->arrow_up = get_icon_pixmap(view, next_sb->gc, arrow_up_src, BUTTON_SIZE, BUTTON_SIZE);
194   next_sb->arrow_down =
195       get_icon_pixmap(view, next_sb->gc, arrow_down_src, BUTTON_SIZE, BUTTON_SIZE);
196   next_sb->arrow_up_pressed =
197       get_icon_pixmap(view, next_sb->gc, arrow_up_pressed_src, BUTTON_SIZE, BUTTON_SIZE);
198   next_sb->arrow_down_pressed =
199       get_icon_pixmap(view, next_sb->gc, arrow_down_pressed_src, BUTTON_SIZE, BUTTON_SIZE);
200 
201   XCopyArea(view->display, next_sb->background, view->window, view->gc, 0, 0, WIDTH, view->height,
202             0, 0);
203 }
204 
resized(ui_sb_view_t * view,Window window,unsigned int height)205 static void resized(ui_sb_view_t *view, Window window, unsigned int height) {
206   next_sb_view_t *next_sb;
207 
208   next_sb = (next_sb_view_t *)view;
209 
210   view->window = window;
211   view->height = height;
212 
213   /* create new background pixmap to fit well with resized scroll view */
214   XFreePixmap(view->display, next_sb->background);
215   next_sb->background = create_bg(view, WIDTH, view->height);
216 }
217 
destroy(ui_sb_view_t * view)218 static void destroy(ui_sb_view_t *view) {
219   next_sb_view_t *next_sb;
220 
221   next_sb = (next_sb_view_t *)view;
222 
223   if (next_sb) {
224     XFreePixmap(view->display, next_sb->background);
225     XFreePixmap(view->display, next_sb->bar_relief);
226     XFreePixmap(view->display, next_sb->arrow_up);
227     XFreePixmap(view->display, next_sb->arrow_up_pressed);
228     XFreePixmap(view->display, next_sb->arrow_down);
229     XFreePixmap(view->display, next_sb->arrow_down_pressed);
230 
231     XFreeGC(view->display, next_sb->gc);
232 
233     free(next_sb);
234   }
235 }
236 
draw_up_button(ui_sb_view_t * view,int is_pressed)237 static void draw_up_button(ui_sb_view_t *view, int is_pressed) {
238   next_sb_view_t *next_sb;
239   Pixmap arrow;
240   char **src;
241   int x;
242   int y;
243 
244   next_sb = (next_sb_view_t *)view;
245 
246   /* clear */
247   if (next_sb->is_transparent) {
248     XClearArea(view->display, view->window, 1, UP_BUTTON_Y(view->height), BUTTON_SIZE, BUTTON_SIZE,
249                0);
250   } else {
251     XCopyArea(view->display, next_sb->background, view->window, view->gc, 0,
252               UP_BUTTON_Y(view->height) - 1, WIDTH, BUTTON_SIZE + 2, 0,
253               UP_BUTTON_Y(view->height) - 1);
254   }
255 
256   /* if no scrollback buffer, not draw */
257   if (!next_sb->has_scrollbuf) {
258     return;
259   }
260 
261   if (is_pressed) {
262     arrow = next_sb->arrow_up_pressed;
263     src = arrow_up_pressed_src;
264   } else {
265     arrow = next_sb->arrow_up;
266     src = arrow_up_src;
267   }
268 
269   /* drowing upper arrow button */
270   if (next_sb->is_transparent) {
271     for (y = 0; y < BUTTON_SIZE; y++) {
272       for (x = 0; x < BUTTON_SIZE; x++) {
273         if (src[y][x] == '-') {
274           XCopyArea(view->display, view->window, arrow, view->gc, x + 1,
275                     y + UP_BUTTON_Y(view->height), 1, 1, x, y);
276         }
277       }
278     }
279   }
280   XCopyArea(view->display, arrow, view->window, view->gc, 0, 0, BUTTON_SIZE, BUTTON_SIZE, 1,
281             UP_BUTTON_Y(view->height));
282 }
283 
draw_down_button(ui_sb_view_t * view,int is_pressed)284 static void draw_down_button(ui_sb_view_t *view, int is_pressed) {
285   next_sb_view_t *next_sb;
286   Pixmap arrow;
287   char **src;
288   int x;
289   int y;
290 
291   next_sb = (next_sb_view_t *)view;
292 
293   /* clear */
294   if (next_sb->is_transparent) {
295     XClearArea(view->display, view->window, 1, DOWN_BUTTON_Y(view->height), BUTTON_SIZE,
296                BUTTON_SIZE, 0);
297   } else {
298     XCopyArea(view->display, next_sb->background, view->window, view->gc, 0,
299               DOWN_BUTTON_Y(view->height), WIDTH, BUTTON_SIZE + 1, 0, DOWN_BUTTON_Y(view->height));
300   }
301 
302   /* if no scrollback buffer, not draw */
303   if (!next_sb->has_scrollbuf) {
304     return;
305   }
306 
307   if (is_pressed) {
308     arrow = next_sb->arrow_down_pressed;
309     src = arrow_down_pressed_src;
310   } else {
311     arrow = next_sb->arrow_down;
312     src = arrow_down_src;
313   }
314 
315   /* drowing down arrow button */
316   if (next_sb->is_transparent) {
317     for (y = 0; y < BUTTON_SIZE; y++) {
318       for (x = 0; x < BUTTON_SIZE; x++) {
319         if (src[y][x] == '-') {
320           XCopyArea(view->display, view->window, arrow, view->gc, x + 1,
321                     y + DOWN_BUTTON_Y(view->height), 1, 1, x, y);
322         }
323       }
324     }
325   }
326   XCopyArea(view->display, arrow, view->window, view->gc, 0, 0, BUTTON_SIZE, BUTTON_SIZE, 1,
327             DOWN_BUTTON_Y(view->height));
328 }
329 
draw_scrollbar(ui_sb_view_t * view,int bar_top_y,unsigned int bar_height)330 static void draw_scrollbar(ui_sb_view_t *view, int bar_top_y, unsigned int bar_height) {
331   next_sb_view_t *next_sb;
332   XSegment line[2];
333 
334   next_sb = (next_sb_view_t *)view;
335 
336   if (bar_top_y == 0 && bar_height == view->height - BOTTOM_MARGIN) {
337     /* drawing scroll view background to clear */
338     /* FIXME: should use XSetWindowBackgroundPixmap() */
339     if (!next_sb->is_transparent) {
340       XCopyArea(view->display, next_sb->background, view->window, view->gc, 0, 0, WIDTH,
341                 view->height - BOTTOM_MARGIN, 0, 0);
342     } else {
343       XClearArea(view->display, view->window, 1, 0, WIDTH - 2, view->height - BOTTOM_MARGIN, 0);
344     }
345     return; /* if no scrollback buffer, not draw bar */
346   }
347 
348   /* rise up/down button */
349   if (next_sb->has_scrollbuf == 0) {
350     next_sb->has_scrollbuf = 1;
351     draw_up_button(view, 0);
352     draw_down_button(view, 0);
353   }
354 
355   /* clear */
356   if (next_sb->is_transparent) {
357     XClearArea(view->display, view->window, 1, 0, WIDTH - 2, view->height - BOTTOM_MARGIN, 0);
358   } else {
359     /* FIXME: should use XSetWindowBackgroundPixmap() */
360     XCopyArea(view->display, next_sb->background, view->window, view->gc, 0, 0, WIDTH, bar_top_y, 0,
361               0);
362     XCopyArea(view->display, next_sb->background, view->window, view->gc, 0, bar_top_y, WIDTH,
363               view->height - bar_top_y - bar_height - BOTTOM_MARGIN, 0, bar_top_y + bar_height);
364     XSetForeground(view->display, next_sb->gc, next_sb->gray_light);
365     line[0].x1 = 0;
366     line[0].y1 = bar_top_y;
367     line[0].x2 = 0;
368     line[0].y2 = bar_top_y + view->height - 1;
369     line[1].x1 = WIDTH - 1;
370     line[1].y1 = bar_top_y;
371     line[1].x2 = WIDTH - 1;
372     line[1].y2 = bar_top_y + bar_height - 1;
373     XDrawSegments(view->display, view->window, next_sb->gc, line, 2);
374   }
375 
376   /* drawing bar */
377   if (!next_sb->is_transparent) {
378     XSetForeground(view->display, next_sb->gc, next_sb->gray_light);
379     XFillRectangle(view->display, view->window, next_sb->gc, 1, bar_top_y, WIDTH - 2, bar_height);
380   }
381 
382   /* drawing relief */
383   if (bar_height >= BAR_RELIEF_SIZE) {
384     XCopyArea(view->display, next_sb->bar_relief, view->window, next_sb->gc, 1, 0,
385               BAR_RELIEF_SIZE - 2, 1, BAR_RELIEF_X + 1,
386               bar_top_y + (bar_height - BAR_RELIEF_SIZE) / 2);
387     XCopyArea(view->display, next_sb->bar_relief, view->window, next_sb->gc, 0, 1, BAR_RELIEF_SIZE,
388               BAR_RELIEF_SIZE - 2, BAR_RELIEF_X,
389               bar_top_y + (bar_height - BAR_RELIEF_SIZE) / 2 + 1);
390     XCopyArea(view->display, next_sb->bar_relief, view->window, next_sb->gc, 1, 5,
391               BAR_RELIEF_SIZE - 2, 1, BAR_RELIEF_X + 1,
392               bar_top_y + (bar_height - BAR_RELIEF_SIZE) / 2 + 5);
393 #if 0
394     XCopyArea(view->display, next_sb->bar_relief, view->window, next_sb->gc, 0, 0, BAR_RELIEF_SIZE,
395               BAR_RELIEF_SIZE, BAR_RELIEF_X, bar_top_y + (bar_height - BAR_RELIEF_SIZE) / 2);
396 #endif
397   }
398 
399   /* bar's highlight */
400   XSetForeground(view->display, next_sb->gc, WhitePixel(view->display, view->screen));
401   line[0].x1 = 1;
402   line[0].y1 = bar_top_y;
403   line[0].x2 = 1;
404   line[0].y2 = bar_top_y + bar_height - 1;
405   line[1].x1 = 2;
406   line[1].y1 = bar_top_y;
407   line[1].x2 = WIDTH - 3;
408   line[1].y2 = bar_top_y;
409   XDrawSegments(view->display, view->window, next_sb->gc, line, 2);
410 
411   /* bar's shade (black) */
412   XSetForeground(view->display, next_sb->gc, BlackPixel(view->display, view->screen));
413   line[0].x1 = WIDTH - 2;
414   line[0].y1 = bar_top_y;
415   line[0].x2 = WIDTH - 2;
416   line[0].y2 = bar_top_y + bar_height - 1;
417   line[1].x1 = 1;
418   line[1].y1 = bar_top_y + bar_height - 1;
419   line[1].x2 = WIDTH - 3;
420   line[1].y2 = bar_top_y + bar_height - 1;
421   XDrawSegments(view->display, view->window, next_sb->gc, line, 2);
422 
423   /* bar's shade (nextish dark gray) */
424   XSetForeground(view->display, next_sb->gc, next_sb->gray_dark);
425   line[0].x1 = WIDTH - 3;
426   line[0].y1 = bar_top_y + 1;
427   line[0].x2 = WIDTH - 3;
428   line[0].y2 = bar_top_y + bar_height - 2;
429   line[1].x1 = 2;
430   line[1].y1 = bar_top_y + bar_height - 2;
431   line[1].x2 = WIDTH - 4;
432   line[1].y2 = bar_top_y + bar_height - 2;
433   XDrawSegments(view->display, view->window, next_sb->gc, line, 2);
434 }
435 
436 /* --- global functions --- */
437 
ui_next_sb_view_new(void)438 ui_sb_view_t *ui_next_sb_view_new(void) {
439   next_sb_view_t *next_sb;
440 
441   if ((next_sb = calloc(1, sizeof(next_sb_view_t))) == NULL) {
442     return NULL;
443   }
444 
445   next_sb->view.version = 1;
446 
447   next_sb->view.get_geometry_hints = get_geometry_hints;
448   next_sb->view.get_default_color = get_default_color;
449   next_sb->view.realized = realized;
450   next_sb->view.resized = resized;
451   next_sb->view.destroy = destroy;
452 
453   next_sb->view.draw_scrollbar = draw_scrollbar;
454 
455   next_sb->view.draw_up_button = draw_up_button;
456   next_sb->view.draw_down_button = draw_down_button;
457 
458   return (ui_sb_view_t *)next_sb;
459 }
460 
ui_next_transparent_sb_view_new(void)461 ui_sb_view_t *ui_next_transparent_sb_view_new(void) {
462   next_sb_view_t *next_sb;
463 
464   if ((next_sb = calloc(1, sizeof(next_sb_view_t))) == NULL) {
465     return NULL;
466   }
467 
468   next_sb->view.version = 1;
469 
470   next_sb->view.get_geometry_hints = get_geometry_hints;
471   next_sb->view.get_default_color = get_default_color;
472   next_sb->view.realized = realized;
473   next_sb->view.resized = resized;
474   next_sb->view.destroy = destroy;
475 
476   next_sb->view.draw_scrollbar = draw_scrollbar;
477 
478   next_sb->view.draw_up_button = draw_up_button;
479   next_sb->view.draw_down_button = draw_down_button;
480 
481   next_sb->is_transparent = 1;
482 
483   return (ui_sb_view_t *)next_sb;
484 }
485