4 * Copyright (c) Tuomo Valkonen 1999-2007.
6 * Ion is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
15 #include <ioncore/global.h>
16 #include <ioncore/common.h>
17 #include <ioncore/gr.h>
22 #include <X11/extensions/shape.h>
25 /*{{{ Colour group lookup */
28 static DEColourGroup *destyle_get_colour_group2(DEStyle *style,
29 const GrStyleSpec *a1,
30 const GrStyleSpec *a2)
32 int i, score, maxscore=0;
33 DEColourGroup *maxg=&(style->cgrp);
36 for(i=0; i<style->n_extra_cgrps; i++){
37 score=gr_stylespec_score2(&style->extra_cgrps[i].spec, a1, a2);
40 maxg=&(style->extra_cgrps[i]);
44 style=style->based_on;
51 DEColourGroup *debrush_get_colour_group2(DEBrush *brush,
52 const GrStyleSpec *a1,
53 const GrStyleSpec *a2)
55 return destyle_get_colour_group2(brush->d, a1, a2);
59 DEColourGroup *debrush_get_colour_group(DEBrush *brush, const GrStyleSpec *attr)
61 return destyle_get_colour_group2(brush->d, attr, NULL);
65 DEColourGroup *debrush_get_current_colour_group(DEBrush *brush)
67 return debrush_get_colour_group(brush, debrush_get_current_attr(brush));
77 /* Draw a border at x, y with outer width w x h. Top and left 'tl' pixels
78 * wide with color 'tlc' and bottom and right 'br' pixels with colors 'brc'.
80 static void do_draw_border(Window win, GC gc, int x, int y, int w, int h,
81 uint tl, uint br, DEColour tlc, DEColour brc)
89 XSetForeground(ioncore_g.dpy, gc, tlc);
96 points[0].x=x+i; points[0].y=y+h+1-b;
97 points[1].x=x+i; points[1].y=y+i;
98 points[2].x=x+w+1-a; points[2].y=y+i;
105 XDrawLines(ioncore_g.dpy, win, gc, points, 3, CoordModeOrigin);
109 XSetForeground(ioncore_g.dpy, gc, brc);
115 points[0].x=x+w-i; points[0].y=y+b;
116 points[1].x=x+w-i; points[1].y=y+h-i;
117 points[2].x=x+a; points[2].y=y+h-i;
124 XDrawLines(ioncore_g.dpy, win, gc, points, 3, CoordModeOrigin);
129 static void draw_border(Window win, GC gc, WRectangle *geom,
130 uint tl, uint br, DEColour tlc, DEColour brc)
132 do_draw_border(win, gc, geom->x, geom->y, geom->w, geom->h,
141 static void draw_borderline(Window win, GC gc, WRectangle *geom,
142 uint tl, uint br, DEColour tlc, DEColour brc,
145 if(line==GR_BORDERLINE_LEFT && geom->h>0 && tl>0){
146 XSetForeground(ioncore_g.dpy, gc, tlc);
147 XSetBackground(ioncore_g.dpy, gc, tlc);
148 XFillRectangle(ioncore_g.dpy, win, gc, geom->x, geom->y, tl, geom->h);
150 }else if(line==GR_BORDERLINE_TOP && geom->w>0 && tl>0){
151 XSetForeground(ioncore_g.dpy, gc, tlc);
152 XSetBackground(ioncore_g.dpy, gc, tlc);
153 XFillRectangle(ioncore_g.dpy, win, gc, geom->x, geom->y, geom->w, tl);
155 }else if(line==GR_BORDERLINE_RIGHT && geom->h>0 && br>0){
156 XSetForeground(ioncore_g.dpy, gc, brc);
157 XSetBackground(ioncore_g.dpy, gc, brc);
158 XFillRectangle(ioncore_g.dpy, win, gc, geom->x+geom->w-br, geom->y, br, geom->h);
160 }else if(line==GR_BORDERLINE_BOTTOM && geom->w>0 && br>0){
161 XSetForeground(ioncore_g.dpy, gc, brc);
162 XSetBackground(ioncore_g.dpy, gc, brc);
163 XFillRectangle(ioncore_g.dpy, win, gc, geom->x, geom->y+geom->h-br, geom->w, br);
169 void debrush_do_draw_borderline(DEBrush *brush, WRectangle geom,
170 DEColourGroup *cg, GrBorderLine line)
172 DEBorder *bd=&(brush->d->border);
173 GC gc=brush->d->normal_gc;
174 Window win=brush->win;
178 draw_borderline(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh, line);
179 case DEBORDER_INLAID:
180 draw_borderline(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad, line);
181 draw_borderline(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl, line);
183 case DEBORDER_GROOVE:
184 draw_borderline(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl, line);
185 draw_borderline(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad, line);
186 draw_borderline(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh, line);
188 case DEBORDER_ELEVATED:
190 draw_borderline(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh, line);
191 draw_borderline(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad, line);
197 void debrush_do_draw_padline(DEBrush *brush, WRectangle geom,
198 DEColourGroup *cg, GrBorderLine line)
200 DEBorder *bd=&(brush->d->border);
201 GC gc=brush->d->normal_gc;
202 Window win=brush->win;
204 draw_borderline(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad, line);
208 void debrush_draw_borderline(DEBrush *brush, const WRectangle *geom,
211 DEColourGroup *cg=debrush_get_current_colour_group(brush);
213 debrush_do_draw_borderline(brush, *geom, cg, line);
217 static void debrush_do_do_draw_border(DEBrush *brush, WRectangle geom,
220 DEBorder *bd=&(brush->d->border);
221 GC gc=brush->d->normal_gc;
222 Window win=brush->win;
226 draw_border(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh);
227 case DEBORDER_INLAID:
228 draw_border(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad);
229 draw_border(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl);
231 case DEBORDER_GROOVE:
232 draw_border(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl);
233 draw_border(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad);
234 draw_border(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh);
236 case DEBORDER_ELEVATED:
238 draw_border(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh);
239 draw_border(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad);
245 void debrush_do_draw_border(DEBrush *brush, WRectangle geom,
248 DEBorder *bd=&(brush->d->border);
252 debrush_do_do_draw_border(brush, geom, cg);
255 debrush_do_draw_padline(brush, geom, cg, GR_BORDERLINE_LEFT);
256 debrush_do_draw_padline(brush, geom, cg, GR_BORDERLINE_RIGHT);
257 debrush_do_draw_borderline(brush, geom, cg, GR_BORDERLINE_TOP);
258 debrush_do_draw_borderline(brush, geom, cg, GR_BORDERLINE_BOTTOM);
261 debrush_do_draw_padline(brush, geom, cg, GR_BORDERLINE_TOP);
262 debrush_do_draw_padline(brush, geom, cg, GR_BORDERLINE_BOTTOM);
263 debrush_do_draw_borderline(brush, geom, cg, GR_BORDERLINE_LEFT);
264 debrush_do_draw_borderline(brush, geom, cg, GR_BORDERLINE_RIGHT);
270 void debrush_draw_border(DEBrush *brush,
271 const WRectangle *geom)
273 DEColourGroup *cg=debrush_get_current_colour_group(brush);
275 debrush_do_draw_border(brush, *geom, cg);
285 static void copy_masked(DEBrush *brush, Drawable src, Drawable dst,
286 int src_x, int src_y, int w, int h,
287 int dst_x, int dst_y)
290 GC copy_gc=brush->d->copy_gc;
292 XSetClipMask(ioncore_g.dpy, copy_gc, src);
293 XSetClipOrigin(ioncore_g.dpy, copy_gc, dst_x, dst_y);
294 XCopyPlane(ioncore_g.dpy, src, dst, copy_gc, src_x, src_y, w, h,
299 static GrStyleSpec dragged_spec=GR_STYLESPEC_INIT;
300 static GrStyleSpec tagged_spec=GR_STYLESPEC_INIT;
301 static GrStyleSpec submenu_spec=GR_STYLESPEC_INIT;
304 void debrush_tab_extras(DEBrush *brush, const WRectangle *g,
305 DEColourGroup *cg, GrBorderWidths *bdw,
307 const GrStyleSpec *a1,
308 const GrStyleSpec *a2,
313 /* Not thread-safe, but neither is the rest of the drawing code
316 static bool swapped=FALSE;
318 ENSURE_INITSPEC(dragged_spec, "dragged");
319 ENSURE_INITSPEC(tagged_spec, "tagged");
322 if(!MATCHES2(dragged_spec, a1, a2))
326 d->normal_gc=d->stipple_gc;
329 XClearArea(ioncore_g.dpy, brush->win, g->x, g->y, g->w, g->h, False);
333 if(MATCHES2(tagged_spec, a1, a2)){
334 XSetForeground(ioncore_g.dpy, d->copy_gc, cg->fg);
336 copy_masked(brush, d->tag_pixmap, brush->win, 0, 0,
337 d->tag_pixmap_w, d->tag_pixmap_h,
338 g->x+g->w-bdw->right-d->tag_pixmap_w,
344 d->normal_gc=d->stipple_gc;
348 /*if(MATCHES2("*-*-*-dragged", a1, a2)){
349 XFillRectangle(ioncore_g.dpy, win, d->stipple_gc,
350 g->x, g->y, g->w, g->h);
355 void debrush_menuentry_extras(DEBrush *brush,
360 const GrStyleSpec *a1,
361 const GrStyleSpec *a2,
369 ENSURE_INITSPEC(submenu_spec, "submenu");
371 if(!MATCHES2(submenu_spec, a1, a2))
374 ty=(g->y+bdw->top+fnte->baseline
375 +(g->h-bdw->top-bdw->bottom-fnte->max_height)/2);
376 tx=g->x+g->w-bdw->right;
378 debrush_do_draw_string(brush, tx, ty, DE_SUB_IND, DE_SUB_IND_LEN,
383 void debrush_do_draw_box(DEBrush *brush, const WRectangle *geom,
384 DEColourGroup *cg, bool needfill)
386 GC gc=brush->d->normal_gc;
388 if(TRUE/*needfill*/){
389 XSetForeground(ioncore_g.dpy, gc, cg->bg);
390 XFillRectangle(ioncore_g.dpy, brush->win, gc, geom->x, geom->y,
394 debrush_do_draw_border(brush, *geom, cg);
398 static void debrush_do_draw_textbox(DEBrush *brush,
399 const WRectangle *geom,
403 const GrStyleSpec *a1,
404 const GrStyleSpec *a2)
411 grbrush_get_border_widths(&(brush->grbrush), &bdw);
412 grbrush_get_font_extents(&(brush->grbrush), &fnte);
414 if(brush->extras_fn!=NULL)
415 brush->extras_fn(brush, geom, cg, &bdw, &fnte, a1, a2, TRUE);
417 debrush_do_draw_box(brush, geom, cg, needfill);
428 if(brush->d->textalign!=DEALIGN_LEFT){
429 tw=grbrush_get_text_width((GrBrush*)brush, text, len);
431 if(brush->d->textalign==DEALIGN_CENTER)
432 tx=geom->x+bdw.left+(geom->w-bdw.left-bdw.right-tw)/2;
434 tx=geom->x+geom->w-bdw.right-tw;
439 ty=(geom->y+bdw.top+fnte.baseline
440 +(geom->h-bdw.top-bdw.bottom-fnte.max_height)/2);
442 debrush_do_draw_string(brush, tx, ty, text, len, FALSE, cg);
445 if(brush->extras_fn!=NULL)
446 brush->extras_fn(brush, geom, cg, &bdw, &fnte, a1, a2, FALSE);
450 void debrush_draw_textbox(DEBrush *brush, const WRectangle *geom,
451 const char *text, bool needfill)
453 GrStyleSpec *attr=debrush_get_current_attr(brush);
454 DEColourGroup *cg=debrush_get_colour_group(brush, attr);
457 debrush_do_draw_textbox(brush, geom, text, cg, needfill,
463 void debrush_draw_textboxes(DEBrush *brush, const WRectangle *geom,
464 int n, const GrTextElem *elem,
467 GrStyleSpec *common_attrib;
473 common_attrib=debrush_get_current_attr(brush);
475 grbrush_get_border_widths(&(brush->grbrush), &bdw);
478 g.w=bdw.left+elem[i].iw+bdw.right;
479 cg=debrush_get_colour_group2(brush, common_attrib, &elem[i].attr);
482 debrush_do_draw_textbox(brush, &g, elem[i].text, cg, needfill,
483 common_attrib, &elem[i].attr);
490 if(bdw.spacing>0 && needfill){
491 XClearArea(ioncore_g.dpy, brush->win, g.x, g.y,
492 brush->d->spacing, g.h, False);
506 void debrush_set_window_shape(DEBrush *brush, bool rough,
507 int n, const WRectangle *rects)
509 XRectangle r[MAXSHAPE];
516 /* n==0 should clear the shape. As there's absolutely no
517 * documentation for XShape (as is typical of all sucky X
518 * extensions), I don't know how the shape should properly
519 * be cleared. Thus we just use a huge rectangle.
524 r[0].width=USHRT_MAX;
525 r[0].height=USHRT_MAX;
530 r[i].width=rects[i].w;
531 r[i].height=rects[i].h;
535 XShapeCombineRectangles(ioncore_g.dpy, brush->win,
536 ShapeBounding, 0, 0, r, n,
541 void debrush_enable_transparency(DEBrush *brush, GrTransparency mode)
543 XSetWindowAttributes attr;
546 if(mode==GR_TRANSPARENCY_DEFAULT)
547 mode=brush->d->transparency_mode;
549 if(mode==GR_TRANSPARENCY_YES){
550 attrflags=CWBackPixmap;
551 attr.background_pixmap=ParentRelative;
553 attrflags=CWBackPixel;
554 attr.background_pixel=brush->d->cgrp.bg;
557 XChangeWindowAttributes(ioncore_g.dpy, brush->win, attrflags, &attr);
558 XClearWindow(ioncore_g.dpy, brush->win);
562 void debrush_fill_area(DEBrush *brush, const WRectangle *geom)
564 DEColourGroup *cg=debrush_get_current_colour_group(brush);
565 GC gc=brush->d->normal_gc;
570 XSetForeground(ioncore_g.dpy, gc, cg->bg);
571 XFillRectangle(ioncore_g.dpy, brush->win, gc,
572 geom->x, geom->y, geom->w, geom->h);
576 void debrush_clear_area(DEBrush *brush, const WRectangle *geom)
578 XClearArea(ioncore_g.dpy, brush->win,
579 geom->x, geom->y, geom->w, geom->h, False);
586 /*{{{ Clipping rectangles */
588 /* Should actually set the clipping rectangle for all GC:s and use
589 * window-specific GC:s to do this correctly...
592 static void debrush_set_clipping_rectangle(DEBrush *brush,
593 const WRectangle *geom)
597 assert(!brush->clip_set);
604 XSetClipRectangles(ioncore_g.dpy, brush->d->normal_gc,
605 0, 0, &rect, 1, Unsorted);
606 brush->clip_set=TRUE;
610 static void debrush_clear_clipping_rectangle(DEBrush *brush)
613 XSetClipMask(ioncore_g.dpy, brush->d->normal_gc, None);
614 brush->clip_set=FALSE;
622 /*{{{ debrush_begin/end */
625 void debrush_begin(DEBrush *brush, const WRectangle *geom, int flags)
628 if(flags&GRBRUSH_AMEND)
629 flags|=GRBRUSH_NO_CLEAR_OK;
631 if(!(flags&GRBRUSH_KEEP_ATTR))
632 debrush_init_attr(brush, NULL);
634 if(!(flags&GRBRUSH_NO_CLEAR_OK))
635 debrush_clear_area(brush, geom);
637 if(flags&GRBRUSH_NEED_CLIP)
638 debrush_set_clipping_rectangle(brush, geom);
642 void debrush_end(DEBrush *brush)
644 debrush_clear_clipping_rectangle(brush);