4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
12 #include <ioncore/global.h>
13 #include <ioncore/common.h>
14 #include <ioncore/gr.h>
15 #include <ioncore/gr-util.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
);
34 for(i
=0; i
<style
->n_extra_cgrps
; i
++){
35 score
=gr_stylespec_score2(&style
->extra_cgrps
[i
].spec
, a1
, a2
);
38 maxg
=&(style
->extra_cgrps
[i
]);
42 style
=style
->based_on
;
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
));
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
)
87 XSetForeground(ioncore_g
.dpy
, gc
, tlc
);
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
;
103 XDrawLines(ioncore_g
.dpy
, win
, gc
, points
, 3, CoordModeOrigin
);
107 XSetForeground(ioncore_g
.dpy
, gc
, brc
);
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
;
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
,
139 static void draw_borderline(Window win
, GC gc
, WRectangle
*geom
,
140 uint tl
, uint br
, DEColour tlc
, DEColour brc
,
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
);
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
);
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
);
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
);
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
;
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
);
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
);
186 case DEBORDER_ELEVATED
:
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
);
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
,
209 DEColourGroup
*cg
=debrush_get_current_colour_group(brush
);
211 debrush_do_draw_borderline(brush
, *geom
, cg
, line
);
215 static void debrush_do_do_draw_border(DEBrush
*brush
, WRectangle geom
,
218 DEBorder
*bd
=&(brush
->d
->border
);
219 GC gc
=brush
->d
->normal_gc
;
220 Window win
=brush
->win
;
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
);
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
);
234 case DEBORDER_ELEVATED
:
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
);
243 void debrush_do_draw_border(DEBrush
*brush
, WRectangle geom
,
246 DEBorder
*bd
=&(brush
->d
->border
);
250 debrush_do_do_draw_border(brush
, geom
, cg
);
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
);
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
);
268 void debrush_draw_border(DEBrush
*brush
,
269 const WRectangle
*geom
)
271 DEColourGroup
*cg
=debrush_get_current_colour_group(brush
);
273 debrush_do_draw_border(brush
, *geom
, cg
);
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
,
298 #define ISSET(S, A) ((S)!=NULL && gr_stylespec_isset(S, A))
304 GR_DEFATTR(numbered
);
305 GR_DEFATTR(tabnumber
);
308 static void ensure_attrs()
311 GR_ALLOCATTR(dragged
);
312 GR_ALLOCATTR(tagged
);
313 GR_ALLOCATTR(submenu
);
314 GR_ALLOCATTR(numbered
);
315 GR_ALLOCATTR(tabnumber
);
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
,
337 /* Not thread-safe, but neither is the rest of the drawing code
340 static bool swapped
=FALSE
;
345 if(ISSET(a2
, GR_ATTR(dragged
)) || ISSET(a1
, GR_ATTR(dragged
))){
347 d
->normal_gc
=d
->stipple_gc
;
350 XClearArea(ioncore_g
.dpy
, brush
->win
, g
->x
, g
->y
, g
->w
, g
->h
, False
);
356 if((ISSET(a1
, GR_ATTR(numbered
)) || ISSET(a2
, GR_ATTR(numbered
)))
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
);
373 libtu_asprintf(&s
, "[%d]", index
+1);
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
382 : g
->x
+g
->w
-bdw
->right
-w
);
383 debrush_do_draw_string(brush
, tx
, ty
, s
, l
, TRUE
, cg
);
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
,
401 d
->normal_gc
=d
->stipple_gc
;
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
,
415 const GrBorderWidths
*bdw
,
416 const GrFontExtents
*fnte
,
417 const GrStyleSpec
*a1
,
418 const GrStyleSpec
*a2
,
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
,
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
,
449 debrush_do_draw_border(brush
, *geom
, cg
);
453 static void debrush_do_draw_textbox(DEBrush
*brush
,
454 const WRectangle
*geom
,
458 const GrStyleSpec
*a1
,
459 const GrStyleSpec
*a2
,
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
);
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;
490 tx
=geom
->x
+geom
->w
-bdw
.right
-tw
;
495 ty
=get_ty(geom
, &bdw
, &fnte
);
497 debrush_do_draw_string(brush
, tx
, ty
, text
, len
, FALSE
, cg
);
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
);
512 debrush_do_draw_textbox(brush
, geom
, text
, cg
, needfill
,
518 void debrush_draw_textboxes(DEBrush
*brush
, const WRectangle
*geom
,
519 int n
, const GrTextElem
*elem
,
522 GrStyleSpec
*common_attrib
;
528 common_attrib
=debrush_get_current_attr(brush
);
530 grbrush_get_border_widths(&(brush
->grbrush
), &bdw
);
533 g
.w
=bdw
.left
+elem
[i
].iw
+bdw
.right
;
534 cg
=debrush_get_colour_group2(brush
, common_attrib
, &elem
[i
].attr
);
537 debrush_do_draw_textbox(brush
, &g
, elem
[i
].text
, cg
, needfill
,
538 common_attrib
, &elem
[i
].attr
, i
);
545 if(bdw
.spacing
>0 && needfill
){
546 XClearArea(ioncore_g
.dpy
, brush
->win
, g
.x
, g
.y
,
547 brush
->d
->spacing
, g
.h
, False
);
561 void debrush_set_window_shape(DEBrush
*brush
, bool rough
,
562 int n
, const WRectangle
*rects
)
564 XRectangle r
[MAXSHAPE
];
567 if(!ioncore_g
.shape_extension
)
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.
582 r
[0].width
=USHRT_MAX
;
583 r
[0].height
=USHRT_MAX
;
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
,
599 void debrush_enable_transparency(DEBrush
*brush
, GrTransparency mode
)
601 XSetWindowAttributes attr
;
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
;
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
;
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
);
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
)
655 assert(!brush
->clip_set
);
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
)
671 XSetClipMask(ioncore_g
.dpy
, brush
->d
->normal_gc
, None
);
672 brush
->clip_set
=FALSE
;
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
);