Also make C99_SOURCE overridable
[notion/jeffpc.git] / de / draw.c
blob114b995223696eac9704351835b7478fe1e930d5
1 /*
2 * ion/de/draw.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <string.h>
10 #include <limits.h>
12 #include <ioncore/global.h>
13 #include <ioncore/common.h>
14 #include <ioncore/gr.h>
15 #include <ioncore/gr-util.h>
16 #include "brush.h"
17 #include "font.h"
18 #include "private.h"
20 #include <X11/extensions/shape.h>
23 /*{{{ Colour group lookup */
26 static DEColourGroup *destyle_get_colour_group2(DEStyle *style,
27 const GrStyleSpec *a1,
28 const GrStyleSpec *a2)
30 int i, score, maxscore=0;
31 DEColourGroup *maxg=&(style->cgrp);
33 while(style!=NULL){
34 for(i=0; i<style->n_extra_cgrps; i++){
35 score=gr_stylespec_score2(&style->extra_cgrps[i].spec, a1, a2);
37 if(score>maxscore){
38 maxg=&(style->extra_cgrps[i]);
39 maxscore=score;
42 style=style->based_on;
45 return maxg;
49 DEColourGroup *debrush_get_colour_group2(DEBrush *brush,
50 const GrStyleSpec *a1,
51 const GrStyleSpec *a2)
53 return destyle_get_colour_group2(brush->d, a1, a2);
57 DEColourGroup *debrush_get_colour_group(DEBrush *brush, const GrStyleSpec *attr)
59 return destyle_get_colour_group2(brush->d, attr, NULL);
63 DEColourGroup *debrush_get_current_colour_group(DEBrush *brush)
65 return debrush_get_colour_group(brush, debrush_get_current_attr(brush));
69 /*}}}*/
72 /*{{{ Borders */
75 /* Draw a border at x, y with outer width w x h. Top and left 'tl' pixels
76 * wide with color 'tlc' and bottom and right 'br' pixels with colors 'brc'.
78 static void do_draw_border(Window win, GC gc, int x, int y, int w, int h,
79 uint tl, uint br, DEColour tlc, DEColour brc)
81 XPoint points[3];
82 uint i=0, a=0, b=0;
84 w--;
85 h--;
87 XSetForeground(ioncore_g.dpy, gc, tlc);
90 a=(br!=0);
91 b=0;
93 for(i=0; i<tl; i++){
94 points[0].x=x+i; points[0].y=y+h+1-b;
95 points[1].x=x+i; points[1].y=y+i;
96 points[2].x=x+w+1-a; points[2].y=y+i;
98 if(a<br)
99 a++;
100 if(b<br)
101 b++;
103 XDrawLines(ioncore_g.dpy, win, gc, points, 3, CoordModeOrigin);
107 XSetForeground(ioncore_g.dpy, gc, brc);
109 a=(tl!=0);
110 b=0;
112 for(i=0; i<br; i++){
113 points[0].x=x+w-i; points[0].y=y+b;
114 points[1].x=x+w-i; points[1].y=y+h-i;
115 points[2].x=x+a; points[2].y=y+h-i;
117 if(a<tl)
118 a++;
119 if(b<tl)
120 b++;
122 XDrawLines(ioncore_g.dpy, win, gc, points, 3, CoordModeOrigin);
127 static void draw_border(Window win, GC gc, WRectangle *geom,
128 uint tl, uint br, DEColour tlc, DEColour brc)
130 do_draw_border(win, gc, geom->x, geom->y, geom->w, geom->h,
131 tl, br, tlc, brc);
132 geom->x+=tl;
133 geom->y+=tl;
134 geom->w-=tl+br;
135 geom->h-=tl+br;
139 static void draw_borderline(Window win, GC gc, WRectangle *geom,
140 uint tl, uint br, DEColour tlc, DEColour brc,
141 GrBorderLine line)
143 if(line==GR_BORDERLINE_LEFT && geom->h>0 && tl>0){
144 XSetForeground(ioncore_g.dpy, gc, tlc);
145 XSetBackground(ioncore_g.dpy, gc, tlc);
146 XFillRectangle(ioncore_g.dpy, win, gc, geom->x, geom->y, tl, geom->h);
147 geom->x+=tl;
148 }else if(line==GR_BORDERLINE_TOP && geom->w>0 && tl>0){
149 XSetForeground(ioncore_g.dpy, gc, tlc);
150 XSetBackground(ioncore_g.dpy, gc, tlc);
151 XFillRectangle(ioncore_g.dpy, win, gc, geom->x, geom->y, geom->w, tl);
152 geom->y+=tl;
153 }else if(line==GR_BORDERLINE_RIGHT && geom->h>0 && br>0){
154 XSetForeground(ioncore_g.dpy, gc, brc);
155 XSetBackground(ioncore_g.dpy, gc, brc);
156 XFillRectangle(ioncore_g.dpy, win, gc, geom->x+geom->w-br, geom->y, br, geom->h);
157 geom->w-=br;
158 }else if(line==GR_BORDERLINE_BOTTOM && geom->w>0 && br>0){
159 XSetForeground(ioncore_g.dpy, gc, brc);
160 XSetBackground(ioncore_g.dpy, gc, brc);
161 XFillRectangle(ioncore_g.dpy, win, gc, geom->x, geom->y+geom->h-br, geom->w, br);
162 geom->h-=br;
167 void debrush_do_draw_borderline(DEBrush *brush, WRectangle geom,
168 DEColourGroup *cg, GrBorderLine line)
170 DEBorder *bd=&(brush->d->border);
171 GC gc=brush->d->normal_gc;
172 Window win=brush->win;
174 switch(bd->style){
175 case DEBORDER_RIDGE:
176 draw_borderline(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh, line);
177 case DEBORDER_INLAID:
178 draw_borderline(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad, line);
179 draw_borderline(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl, line);
180 break;
181 case DEBORDER_GROOVE:
182 draw_borderline(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl, line);
183 draw_borderline(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad, line);
184 draw_borderline(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh, line);
185 break;
186 case DEBORDER_ELEVATED:
187 default:
188 draw_borderline(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh, line);
189 draw_borderline(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad, line);
190 break;
195 void debrush_do_draw_padline(DEBrush *brush, WRectangle geom,
196 DEColourGroup *cg, GrBorderLine line)
198 DEBorder *bd=&(brush->d->border);
199 GC gc=brush->d->normal_gc;
200 Window win=brush->win;
202 draw_borderline(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad, line);
206 void debrush_draw_borderline(DEBrush *brush, const WRectangle *geom,
207 GrBorderLine line)
209 DEColourGroup *cg=debrush_get_current_colour_group(brush);
210 if(cg!=NULL)
211 debrush_do_draw_borderline(brush, *geom, cg, line);
215 static void debrush_do_do_draw_border(DEBrush *brush, WRectangle geom,
216 DEColourGroup *cg)
218 DEBorder *bd=&(brush->d->border);
219 GC gc=brush->d->normal_gc;
220 Window win=brush->win;
222 switch(bd->style){
223 case DEBORDER_RIDGE:
224 draw_border(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh);
225 case DEBORDER_INLAID:
226 draw_border(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad);
227 draw_border(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl);
228 break;
229 case DEBORDER_GROOVE:
230 draw_border(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl);
231 draw_border(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad);
232 draw_border(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh);
233 break;
234 case DEBORDER_ELEVATED:
235 default:
236 draw_border(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh);
237 draw_border(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad);
238 break;
243 void debrush_do_draw_border(DEBrush *brush, WRectangle geom,
244 DEColourGroup *cg)
246 DEBorder *bd=&(brush->d->border);
248 switch(bd->sides){
249 case DEBORDER_ALL:
250 debrush_do_do_draw_border(brush, geom, cg);
251 break;
252 case DEBORDER_TB:
253 debrush_do_draw_padline(brush, geom, cg, GR_BORDERLINE_LEFT);
254 debrush_do_draw_padline(brush, geom, cg, GR_BORDERLINE_RIGHT);
255 debrush_do_draw_borderline(brush, geom, cg, GR_BORDERLINE_TOP);
256 debrush_do_draw_borderline(brush, geom, cg, GR_BORDERLINE_BOTTOM);
257 break;
258 case DEBORDER_LR:
259 debrush_do_draw_padline(brush, geom, cg, GR_BORDERLINE_TOP);
260 debrush_do_draw_padline(brush, geom, cg, GR_BORDERLINE_BOTTOM);
261 debrush_do_draw_borderline(brush, geom, cg, GR_BORDERLINE_LEFT);
262 debrush_do_draw_borderline(brush, geom, cg, GR_BORDERLINE_RIGHT);
263 break;
268 void debrush_draw_border(DEBrush *brush,
269 const WRectangle *geom)
271 DEColourGroup *cg=debrush_get_current_colour_group(brush);
272 if(cg!=NULL)
273 debrush_do_draw_border(brush, *geom, cg);
277 /*}}}*/
280 /*{{{ Boxes */
283 static void copy_masked(DEBrush *brush, Drawable src, Drawable dst,
284 int src_x, int src_y, int w, int h,
285 int dst_x, int dst_y)
288 GC copy_gc=brush->d->copy_gc;
290 XSetClipMask(ioncore_g.dpy, copy_gc, src);
291 XSetClipOrigin(ioncore_g.dpy, copy_gc, dst_x, dst_y);
292 XCopyPlane(ioncore_g.dpy, src, dst, copy_gc, src_x, src_y, w, h,
293 dst_x, dst_y, 1);
298 #define ISSET(S, A) ((S)!=NULL && gr_stylespec_isset(S, A))
301 GR_DEFATTR(dragged);
302 GR_DEFATTR(tagged);
303 GR_DEFATTR(submenu);
304 GR_DEFATTR(numbered);
305 GR_DEFATTR(tabnumber);
308 static void ensure_attrs()
310 GR_ALLOCATTR_BEGIN;
311 GR_ALLOCATTR(dragged);
312 GR_ALLOCATTR(tagged);
313 GR_ALLOCATTR(submenu);
314 GR_ALLOCATTR(numbered);
315 GR_ALLOCATTR(tabnumber);
316 GR_ALLOCATTR_END;
320 static int get_ty(const WRectangle *g, const GrBorderWidths *bdw,
321 const GrFontExtents *fnte)
323 return (g->y+bdw->top+fnte->baseline
324 +(g->h-bdw->top-bdw->bottom-fnte->max_height)/2);
328 void debrush_tab_extras(DEBrush *brush, const WRectangle *g,
329 DEColourGroup *cg, const GrBorderWidths *bdw,
330 const GrFontExtents *fnte,
331 const GrStyleSpec *a1,
332 const GrStyleSpec *a2,
333 bool pre, int index)
335 DEStyle *d=brush->d;
336 GC tmp;
337 /* Not thread-safe, but neither is the rest of the drawing code
338 * with shared GC:s.
340 static bool swapped=FALSE;
342 ensure_attrs();
344 if(pre){
345 if(ISSET(a2, GR_ATTR(dragged)) || ISSET(a1, GR_ATTR(dragged))){
346 tmp=d->normal_gc;
347 d->normal_gc=d->stipple_gc;
348 d->stipple_gc=tmp;
349 swapped=TRUE;
350 XClearArea(ioncore_g.dpy, brush->win, g->x, g->y, g->w, g->h, False);
352 return;
356 if((ISSET(a1, GR_ATTR(numbered)) || ISSET(a2, GR_ATTR(numbered)))
357 && index>=0){
359 DEColourGroup *cg;
360 GrStyleSpec tmp;
362 gr_stylespec_init(&tmp);
363 gr_stylespec_append(&tmp, a2);
364 gr_stylespec_set(&tmp, GR_ATTR(tabnumber));
366 cg=debrush_get_colour_group2(brush, a1, &tmp);
368 gr_stylespec_unalloc(&tmp);
370 if(cg!=NULL){
371 char *s=NULL;
373 libtu_asprintf(&s, "[%d]", index+1);
375 if(s!=NULL){
376 int l=strlen(s);
377 uint w=debrush_get_text_width(brush, s, l);
378 if(w < g->w-bdw->right-bdw->left){
379 int ty=get_ty(g, bdw, fnte);
380 int tx=(d->textalign==DEALIGN_RIGHT
381 ? g->x+bdw->left
382 : g->x+g->w-bdw->right-w);
383 debrush_do_draw_string(brush, tx, ty, s, l, TRUE, cg);
385 free(s);
390 if(ISSET(a2, GR_ATTR(tagged)) || ISSET(a1, GR_ATTR(tagged))){
391 XSetForeground(ioncore_g.dpy, d->copy_gc, cg->fg);
393 copy_masked(brush, d->tag_pixmap, brush->win, 0, 0,
394 d->tag_pixmap_w, d->tag_pixmap_h,
395 g->x+g->w-bdw->right-d->tag_pixmap_w,
396 g->y+bdw->top);
399 if(swapped){
400 tmp=d->normal_gc;
401 d->normal_gc=d->stipple_gc;
402 d->stipple_gc=tmp;
403 swapped=FALSE;
405 /*if(MATCHES2("*-*-*-dragged", a1, a2)){
406 XFillRectangle(ioncore_g.dpy, win, d->stipple_gc,
407 g->x, g->y, g->w, g->h);
412 void debrush_menuentry_extras(DEBrush *brush,
413 const WRectangle *g,
414 DEColourGroup *cg,
415 const GrBorderWidths *bdw,
416 const GrFontExtents *fnte,
417 const GrStyleSpec *a1,
418 const GrStyleSpec *a2,
419 bool pre, int index)
421 int tx, ty;
423 if(pre)
424 return;
426 ensure_attrs();
428 if(ISSET(a2, GR_ATTR(submenu)) || ISSET(a1, GR_ATTR(submenu))){
429 ty=get_ty(g, bdw, fnte);
430 tx=g->x+g->w-bdw->right;
432 debrush_do_draw_string(brush, tx, ty, DE_SUB_IND, DE_SUB_IND_LEN,
433 FALSE, cg);
438 void debrush_do_draw_box(DEBrush *brush, const WRectangle *geom,
439 DEColourGroup *cg, bool needfill)
441 GC gc=brush->d->normal_gc;
443 if(TRUE/*needfill*/){
444 XSetForeground(ioncore_g.dpy, gc, cg->bg);
445 XFillRectangle(ioncore_g.dpy, brush->win, gc, geom->x, geom->y,
446 geom->w, geom->h);
449 debrush_do_draw_border(brush, *geom, cg);
453 static void debrush_do_draw_textbox(DEBrush *brush,
454 const WRectangle *geom,
455 const char *text,
456 DEColourGroup *cg,
457 bool needfill,
458 const GrStyleSpec *a1,
459 const GrStyleSpec *a2,
460 int index)
462 uint len;
463 GrBorderWidths bdw;
464 GrFontExtents fnte;
465 uint tx, ty, tw;
467 grbrush_get_border_widths(&(brush->grbrush), &bdw);
468 grbrush_get_font_extents(&(brush->grbrush), &fnte);
470 if(brush->extras_fn!=NULL)
471 brush->extras_fn(brush, geom, cg, &bdw, &fnte, a1, a2, TRUE, index);
473 debrush_do_draw_box(brush, geom, cg, needfill);
475 do{ /*...while(0)*/
476 if(text==NULL)
477 break;
479 len=strlen(text);
481 if(len==0)
482 break;
484 if(brush->d->textalign!=DEALIGN_LEFT){
485 tw=grbrush_get_text_width((GrBrush*)brush, text, len);
487 if(brush->d->textalign==DEALIGN_CENTER)
488 tx=geom->x+bdw.left+(geom->w-bdw.left-bdw.right-tw)/2;
489 else
490 tx=geom->x+geom->w-bdw.right-tw;
491 }else{
492 tx=geom->x+bdw.left;
495 ty=get_ty(geom, &bdw, &fnte);
497 debrush_do_draw_string(brush, tx, ty, text, len, FALSE, cg);
498 }while(0);
500 if(brush->extras_fn!=NULL)
501 brush->extras_fn(brush, geom, cg, &bdw, &fnte, a1, a2, FALSE, index);
505 void debrush_draw_textbox(DEBrush *brush, const WRectangle *geom,
506 const char *text, bool needfill)
508 GrStyleSpec *attr=debrush_get_current_attr(brush);
509 DEColourGroup *cg=debrush_get_colour_group(brush, attr);
511 if(cg!=NULL){
512 debrush_do_draw_textbox(brush, geom, text, cg, needfill,
513 attr, NULL, -1);
518 void debrush_draw_textboxes(DEBrush *brush, const WRectangle *geom,
519 int n, const GrTextElem *elem,
520 bool needfill)
522 GrStyleSpec *common_attrib;
523 WRectangle g=*geom;
524 DEColourGroup *cg;
525 GrBorderWidths bdw;
526 int i;
528 common_attrib=debrush_get_current_attr(brush);
530 grbrush_get_border_widths(&(brush->grbrush), &bdw);
532 for(i=0; ; i++){
533 g.w=bdw.left+elem[i].iw+bdw.right;
534 cg=debrush_get_colour_group2(brush, common_attrib, &elem[i].attr);
536 if(cg!=NULL){
537 debrush_do_draw_textbox(brush, &g, elem[i].text, cg, needfill,
538 common_attrib, &elem[i].attr, i);
541 if(i==n-1)
542 break;
544 g.x+=g.w;
545 if(bdw.spacing>0 && needfill){
546 XClearArea(ioncore_g.dpy, brush->win, g.x, g.y,
547 brush->d->spacing, g.h, False);
549 g.x+=bdw.spacing;
554 /*}}}*/
557 /*{{{ Misc. */
559 #define MAXSHAPE 16
561 void debrush_set_window_shape(DEBrush *brush, bool rough,
562 int n, const WRectangle *rects)
564 XRectangle r[MAXSHAPE];
565 int i;
567 if(!ioncore_g.shape_extension)
568 return;
570 if(n>MAXSHAPE)
571 n=MAXSHAPE;
573 if(n==0){
574 /* n==0 should clear the shape. As there's absolutely no
575 * documentation for XShape (as is typical of all sucky X
576 * extensions), I don't know how the shape should properly
577 * be cleared. Thus we just use a huge rectangle.
579 n=1;
580 r[0].x=0;
581 r[0].y=0;
582 r[0].width=USHRT_MAX;
583 r[0].height=USHRT_MAX;
584 }else{
585 for(i=0; i<n; i++){
586 r[i].x=rects[i].x;
587 r[i].y=rects[i].y;
588 r[i].width=rects[i].w;
589 r[i].height=rects[i].h;
593 XShapeCombineRectangles(ioncore_g.dpy, brush->win,
594 ShapeBounding, 0, 0, r, n,
595 ShapeSet, Unsorted);
599 void debrush_enable_transparency(DEBrush *brush, GrTransparency mode)
601 XSetWindowAttributes attr;
602 ulong attrflags=0;
604 if(mode==GR_TRANSPARENCY_DEFAULT)
605 mode=brush->d->transparency_mode;
607 if(mode==GR_TRANSPARENCY_YES){
608 attrflags=CWBackPixmap;
609 attr.background_pixmap=ParentRelative;
610 }else{
611 attrflags=CWBackPixel;
612 attr.background_pixel=brush->d->cgrp.bg;
615 XChangeWindowAttributes(ioncore_g.dpy, brush->win, attrflags, &attr);
616 XClearWindow(ioncore_g.dpy, brush->win);
620 void debrush_fill_area(DEBrush *brush, const WRectangle *geom)
622 DEColourGroup *cg=debrush_get_current_colour_group(brush);
623 GC gc=brush->d->normal_gc;
625 if(cg==NULL)
626 return;
628 XSetForeground(ioncore_g.dpy, gc, cg->bg);
629 XFillRectangle(ioncore_g.dpy, brush->win, gc,
630 geom->x, geom->y, geom->w, geom->h);
634 void debrush_clear_area(DEBrush *brush, const WRectangle *geom)
636 XClearArea(ioncore_g.dpy, brush->win,
637 geom->x, geom->y, geom->w, geom->h, False);
641 /*}}}*/
644 /*{{{ Clipping rectangles */
646 /* Should actually set the clipping rectangle for all GC:s and use
647 * window-specific GC:s to do this correctly...
650 static void debrush_set_clipping_rectangle(DEBrush *brush,
651 const WRectangle *geom)
653 XRectangle rect;
655 assert(!brush->clip_set);
657 rect.x=geom->x;
658 rect.y=geom->y;
659 rect.width=geom->w;
660 rect.height=geom->h;
662 XSetClipRectangles(ioncore_g.dpy, brush->d->normal_gc,
663 0, 0, &rect, 1, Unsorted);
664 brush->clip_set=TRUE;
668 static void debrush_clear_clipping_rectangle(DEBrush *brush)
670 if(brush->clip_set){
671 XSetClipMask(ioncore_g.dpy, brush->d->normal_gc, None);
672 brush->clip_set=FALSE;
677 /*}}}*/
680 /*{{{ debrush_begin/end */
683 void debrush_begin(DEBrush *brush, const WRectangle *geom, int flags)
686 if(flags&GRBRUSH_AMEND)
687 flags|=GRBRUSH_NO_CLEAR_OK;
689 if(!(flags&GRBRUSH_KEEP_ATTR))
690 debrush_init_attr(brush, NULL);
692 if(!(flags&GRBRUSH_NO_CLEAR_OK))
693 debrush_clear_area(brush, geom);
695 if(flags&GRBRUSH_NEED_CLIP)
696 debrush_set_clipping_rectangle(brush, geom);
700 void debrush_end(DEBrush *brush)
702 debrush_clear_clipping_rectangle(brush);
706 /*}}}*/