4 * Copyright (c) Tuomo Valkonen 1999-2006.
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,
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,
40 maxg=&(style->extra_cgrps[i]);
44 style=style->based_on;
51 DEColourGroup *debrush_get_colour_group2(DEBrush *brush,
55 return destyle_get_colour_group2(brush->d, attr_p1, attr_p2);
59 DEColourGroup *debrush_get_colour_group(DEBrush *brush, const char *attr)
61 return destyle_get_colour_group2(brush->d, attr, NULL);
71 /* Draw a border at x, y with outer width w x h. Top and left 'tl' pixels
72 * wide with color 'tlc' and bottom and right 'br' pixels with colors 'brc'.
74 static void do_draw_border(Window win, GC gc, int x, int y, int w, int h,
75 uint tl, uint br, DEColour tlc, DEColour brc)
83 XSetForeground(ioncore_g.dpy, gc, tlc);
90 points[0].x=x+i; points[0].y=y+h+1-b;
91 points[1].x=x+i; points[1].y=y+i;
92 points[2].x=x+w+1-a; points[2].y=y+i;
99 XDrawLines(ioncore_g.dpy, win, gc, points, 3, CoordModeOrigin);
103 XSetForeground(ioncore_g.dpy, gc, brc);
109 points[0].x=x+w-i; points[0].y=y+b;
110 points[1].x=x+w-i; points[1].y=y+h-i;
111 points[2].x=x+a; points[2].y=y+h-i;
118 XDrawLines(ioncore_g.dpy, win, gc, points, 3, CoordModeOrigin);
123 static void draw_border(Window win, GC gc, WRectangle *geom,
124 uint tl, uint br, DEColour tlc, DEColour brc)
126 do_draw_border(win, gc, geom->x, geom->y, geom->w, geom->h,
135 void debrush_do_draw_border(DEBrush *brush, WRectangle geom,
138 DEBorder *bd=&(brush->d->border);
139 GC gc=brush->d->normal_gc;
140 Window win=brush->win;
144 draw_border(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh);
145 case DEBORDER_INLAID:
146 draw_border(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad);
147 draw_border(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl);
149 case DEBORDER_GROOVE:
150 draw_border(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl);
151 draw_border(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad);
152 draw_border(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh);
154 case DEBORDER_ELEVATED:
156 draw_border(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh);
157 draw_border(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad);
163 void debrush_draw_border(DEBrush *brush,
164 const WRectangle *geom,
167 DEColourGroup *cg=debrush_get_colour_group(brush, attrib);
169 debrush_do_draw_border(brush, *geom, cg);
173 static void draw_borderline(Window win, GC gc, WRectangle *geom,
174 uint tl, uint br, DEColour tlc, DEColour brc,
177 if(line==GR_BORDERLINE_LEFT && geom->h>0){
178 XSetForeground(ioncore_g.dpy, gc, tlc);
179 XDrawRectangle(ioncore_g.dpy, win, gc, geom->x, geom->y, tl, geom->h);
181 }else if(line==GR_BORDERLINE_TOP && geom->w>0){
182 XSetForeground(ioncore_g.dpy, gc, tlc);
183 XDrawRectangle(ioncore_g.dpy, win, gc, geom->x, geom->y, geom->w, tl);
185 }else if(line==GR_BORDERLINE_RIGHT && geom->h>0){
186 XSetForeground(ioncore_g.dpy, gc, brc);
187 XDrawRectangle(ioncore_g.dpy, win, gc, geom->x+geom->w-1-br, geom->y, br, geom->h);
189 }else if(line==GR_BORDERLINE_BOTTOM && geom->w>0){
190 XSetForeground(ioncore_g.dpy, gc, brc);
191 XDrawRectangle(ioncore_g.dpy, win, gc, geom->x, geom->y+geom->h-1-br, geom->w, br);
197 void debrush_do_draw_borderline(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;
206 draw_borderline(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh, line);
207 case DEBORDER_INLAID:
208 draw_borderline(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad, line);
209 draw_borderline(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl, line);
211 case DEBORDER_GROOVE:
212 draw_borderline(win, gc, &geom, bd->sh, bd->hl, cg->sh, cg->hl, line);
213 draw_borderline(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad, line);
214 draw_borderline(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh, line);
216 case DEBORDER_ELEVATED:
218 draw_borderline(win, gc, &geom, bd->hl, bd->sh, cg->hl, cg->sh, line);
219 draw_borderline(win, gc, &geom, bd->pad, bd->pad, cg->pad, cg->pad, line);
225 void debrush_draw_borderline(DEBrush *brush, const WRectangle *geom,
226 const char *attrib, GrBorderLine line)
228 DEColourGroup *cg=debrush_get_colour_group(brush, attrib);
230 debrush_do_draw_borderline(brush, *geom, cg, line);
240 static void copy_masked(DEBrush *brush, Drawable src, Drawable dst,
241 int src_x, int src_y, int w, int h,
242 int dst_x, int dst_y)
245 GC copy_gc=brush->d->copy_gc;
247 XSetClipMask(ioncore_g.dpy, copy_gc, src);
248 XSetClipOrigin(ioncore_g.dpy, copy_gc, dst_x, dst_y);
249 XCopyPlane(ioncore_g.dpy, src, dst, copy_gc, src_x, src_y, w, h,
254 void debrush_tab_extras(DEBrush *brush, const WRectangle *g,
255 DEColourGroup *cg, GrBorderWidths *bdw,
257 const char *a1, const char *a2,
262 /* Not thread-safe, but neither is the rest of the drawing code
265 static bool swapped=FALSE;
268 if(!MATCHES2("*-*-*-dragged", a1, a2))
272 d->normal_gc=d->stipple_gc;
275 XClearArea(ioncore_g.dpy, brush->win, g->x, g->y, g->w, g->h, False);
279 if(MATCHES2("*-*-tagged", a1, a2)){
280 XSetForeground(ioncore_g.dpy, d->copy_gc, cg->fg);
282 copy_masked(brush, d->tag_pixmap, brush->win, 0, 0,
283 d->tag_pixmap_w, d->tag_pixmap_h,
284 g->x+g->w-bdw->right-d->tag_pixmap_w,
290 d->normal_gc=d->stipple_gc;
294 /*if(MATCHES2("*-*-*-dragged", a1, a2)){
295 XFillRectangle(ioncore_g.dpy, win, d->stipple_gc,
296 g->x, g->y, g->w, g->h);
301 void debrush_menuentry_extras(DEBrush *brush, const WRectangle *g,
302 DEColourGroup *cg, GrBorderWidths *bdw,
304 const char *a1, const char *a2,
312 if(!MATCHES2("*-*-submenu", a1, a2))
315 ty=(g->y+bdw->top+fnte->baseline
316 +(g->h-bdw->top-bdw->bottom-fnte->max_height)/2);
317 tx=g->x+g->w-bdw->right;
319 debrush_do_draw_string(brush, tx, ty, DE_SUB_IND, DE_SUB_IND_LEN,
324 void debrush_do_draw_box(DEBrush *brush, const WRectangle *geom,
325 DEColourGroup *cg, bool needfill)
327 GC gc=brush->d->normal_gc;
329 if(TRUE/*needfill*/){
330 XSetForeground(ioncore_g.dpy, gc, cg->bg);
331 XFillRectangle(ioncore_g.dpy, brush->win, gc, geom->x, geom->y,
335 debrush_do_draw_border(brush, *geom, cg);
339 static void debrush_do_draw_textbox(DEBrush *brush, const WRectangle *geom,
340 const char *text, DEColourGroup *cg,
342 const char *a1, const char *a2)
349 grbrush_get_border_widths(&(brush->grbrush), &bdw);
350 grbrush_get_font_extents(&(brush->grbrush), &fnte);
352 if(brush->extras_fn!=NULL)
353 brush->extras_fn(brush, geom, cg, &bdw, &fnte, a1, a2, TRUE);
355 debrush_do_draw_box(brush, geom, cg, needfill);
366 if(brush->d->textalign!=DEALIGN_LEFT){
367 tw=grbrush_get_text_width((GrBrush*)brush, text, len);
369 if(brush->d->textalign==DEALIGN_CENTER)
370 tx=geom->x+bdw.left+(geom->w-bdw.left-bdw.right-tw)/2;
372 tx=geom->x+geom->w-bdw.right-tw;
377 ty=(geom->y+bdw.top+fnte.baseline
378 +(geom->h-bdw.top-bdw.bottom-fnte.max_height)/2);
380 debrush_do_draw_string(brush, tx, ty, text, len, FALSE, cg);
383 if(brush->extras_fn!=NULL)
384 brush->extras_fn(brush, geom, cg, &bdw, &fnte, a1, a2, FALSE);
388 void debrush_draw_textbox(DEBrush *brush, const WRectangle *geom,
389 const char *text, const char *attr,
392 DEColourGroup *cg=debrush_get_colour_group(brush, attr);
394 debrush_do_draw_textbox(brush, geom, text, cg, needfill,
400 void debrush_draw_textboxes(DEBrush *brush, const WRectangle *geom,
401 int n, const GrTextElem *elem,
402 bool needfill, const char *common_attrib)
409 grbrush_get_border_widths(&(brush->grbrush), &bdw);
412 g.w=bdw.left+elem[i].iw+bdw.right;
413 cg=debrush_get_colour_group2(brush, common_attrib, elem[i].attr);
416 debrush_do_draw_textbox(brush, &g, elem[i].text, cg, needfill,
417 common_attrib, elem[i].attr);
421 if(bdw.spacing>0 && needfill){
422 XClearArea(ioncore_g.dpy, brush->win, g.x, g.y,
423 brush->d->spacing, g.h, False);
437 void debrush_set_window_shape(DEBrush *brush, bool rough,
438 int n, const WRectangle *rects)
440 XRectangle r[MAXSHAPE];
447 /* n==0 should clear the shape. As there's absolutely no
448 * documentation for XShape (as is typical of all sucky X
449 * extensions), I don't know how the shape should properly
450 * be cleared. Thus we just use a huge rectangle.
455 r[0].width=USHRT_MAX;
456 r[0].height=USHRT_MAX;
461 r[i].width=rects[i].w;
462 r[i].height=rects[i].h;
466 XShapeCombineRectangles(ioncore_g.dpy, brush->win,
467 ShapeBounding, 0, 0, r, n,
472 void debrush_enable_transparency(DEBrush *brush, GrTransparency mode)
474 XSetWindowAttributes attr;
477 if(mode==GR_TRANSPARENCY_DEFAULT)
478 mode=brush->d->transparency_mode;
480 if(mode==GR_TRANSPARENCY_YES){
481 attrflags=CWBackPixmap;
482 attr.background_pixmap=ParentRelative;
484 attrflags=CWBackPixel;
485 attr.background_pixel=brush->d->cgrp.bg;
488 XChangeWindowAttributes(ioncore_g.dpy, brush->win, attrflags, &attr);
489 XClearWindow(ioncore_g.dpy, brush->win);
493 void debrush_fill_area(DEBrush *brush, const WRectangle *geom, const char *attr)
495 DEColourGroup *cg=debrush_get_colour_group(brush, attr);
496 GC gc=brush->d->normal_gc;
501 XSetForeground(ioncore_g.dpy, gc, cg->bg);
502 XFillRectangle(ioncore_g.dpy, brush->win, gc,
503 geom->x, geom->y, geom->w, geom->h);
507 void debrush_clear_area(DEBrush *brush, const WRectangle *geom)
509 XClearArea(ioncore_g.dpy, brush->win,
510 geom->x, geom->y, geom->w, geom->h, False);
517 /*{{{ Clipping rectangles */
519 /* Should actually set the clipping rectangle for all GC:s and use
520 * window-specific GC:s to do this correctly...
523 static void debrush_set_clipping_rectangle(DEBrush *brush,
524 const WRectangle *geom)
528 assert(!brush->clip_set);
535 XSetClipRectangles(ioncore_g.dpy, brush->d->normal_gc,
536 0, 0, &rect, 1, Unsorted);
537 brush->clip_set=TRUE;
541 static void debrush_clear_clipping_rectangle(DEBrush *brush)
544 XSetClipMask(ioncore_g.dpy, brush->d->normal_gc, None);
545 brush->clip_set=FALSE;
553 /*{{{ debrush_begin/end */
556 void debrush_begin(DEBrush *brush, const WRectangle *geom, int flags)
558 if(flags&GRBRUSH_AMEND)
559 flags|=GRBRUSH_NO_CLEAR_OK;
561 if(!(flags&GRBRUSH_NO_CLEAR_OK))
562 debrush_clear_area(brush, geom);
564 if(flags&GRBRUSH_NEED_CLIP)
565 debrush_set_clipping_rectangle(brush, geom);
569 void debrush_end(DEBrush *brush)
571 debrush_clear_clipping_rectangle(brush);