]> git.decadent.org.uk Git - ion3.git/blob - ioncore/frame-draw.c
Imported upstream version 20090526
[ion3.git] / ioncore / frame-draw.c
1 /*
2  * ion/ioncore/frame-draw.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2009. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <string.h>
10
11 #include <libtu/objp.h>
12 #include <libtu/minmax.h>
13 #include <libtu/map.h>
14
15 #include "common.h"
16 #include "frame.h"
17 #include "framep.h"
18 #include "frame-draw.h"
19 #include "strings.h"
20 #include "activity.h"
21 #include "names.h"
22 #include "gr.h"
23 #include "gr-util.h"
24
25
26 #define BAR_INSIDE_BORDER(FRAME) \
27     ((FRAME)->barmode==FRAME_BAR_INSIDE || (FRAME)->barmode==FRAME_BAR_NONE)
28 #define BAR_EXISTS(FRAME) ((FRAME)->barmode!=FRAME_BAR_NONE)
29 #define BAR_H(FRAME) (FRAME)->bar_h
30
31
32 /*{{{ Attributes */
33
34
35 GR_DEFATTR(active);
36 GR_DEFATTR(inactive);
37 GR_DEFATTR(selected);
38 GR_DEFATTR(unselected);
39 GR_DEFATTR(tagged);
40 GR_DEFATTR(not_tagged);
41 GR_DEFATTR(dragged);
42 GR_DEFATTR(not_dragged);
43 GR_DEFATTR(activity);
44 GR_DEFATTR(no_activity);
45
46
47 static void ensure_create_attrs()
48 {
49     GR_ALLOCATTR_BEGIN;
50     GR_ALLOCATTR(active);
51     GR_ALLOCATTR(inactive);
52     GR_ALLOCATTR(selected);
53     GR_ALLOCATTR(unselected);
54     GR_ALLOCATTR(tagged);
55     GR_ALLOCATTR(not_tagged);
56     GR_ALLOCATTR(dragged);
57     GR_ALLOCATTR(not_dragged);
58     GR_ALLOCATTR(no_activity);
59     GR_ALLOCATTR(activity);
60     GR_ALLOCATTR_END;
61 }
62     
63
64 void frame_update_attr(WFrame *frame, int i, WRegion *reg)
65 {
66     GrStyleSpec *spec;
67     bool selected, tagged, dragged, activity;
68     
69     if(i>=frame->titles_n){
70         /* Might happen when deinitialising */
71         return;
72     }
73     
74     ensure_create_attrs();
75     
76     spec=&frame->titles[i].attr;
77     
78     selected=(reg==FRAME_CURRENT(frame));
79     tagged=(reg!=NULL && reg->flags&REGION_TAGGED);
80     dragged=(i==frame->tab_dragged_idx);
81     activity=(reg!=NULL && region_is_activity_r(reg));
82     
83     gr_stylespec_unalloc(spec);
84     gr_stylespec_set(spec, selected ? GR_ATTR(selected) : GR_ATTR(unselected));
85     gr_stylespec_set(spec, tagged ? GR_ATTR(tagged) : GR_ATTR(not_tagged));
86     gr_stylespec_set(spec, dragged ? GR_ATTR(dragged) : GR_ATTR(not_dragged));
87     gr_stylespec_set(spec, activity ? GR_ATTR(activity) : GR_ATTR(no_activity));
88 }
89
90
91 /*}}}*/
92
93
94 /*{{{ (WFrame) dynfun default implementations */
95
96
97 static uint get_spacing(const WFrame *frame)
98 {
99     GrBorderWidths bdw;
100     
101     if(frame->brush==NULL)
102         return 0;
103     
104     grbrush_get_border_widths(frame->brush, &bdw);
105     
106     return bdw.spacing;
107 }
108
109
110 void frame_border_geom(const WFrame *frame, WRectangle *geom)
111 {
112     geom->x=0;
113     geom->y=0;
114     geom->w=REGION_GEOM(frame).w;
115     geom->h=REGION_GEOM(frame).h;
116     
117     if(!BAR_INSIDE_BORDER(frame) && frame->brush!=NULL){
118         geom->y+=frame->bar_h;
119         geom->h=maxof(0, geom->h-frame->bar_h);
120     }
121 }
122
123
124 void frame_border_inner_geom(const WFrame *frame, WRectangle *geom)
125 {
126     GrBorderWidths bdw;
127     
128     frame_border_geom(frame, geom);
129
130     if(frame->brush!=NULL){
131         grbrush_get_border_widths(frame->brush, &bdw);
132
133         geom->x+=bdw.left;
134         geom->y+=bdw.top;
135         geom->w=maxof(0, geom->w-(bdw.left+bdw.right));
136         geom->h=maxof(0, geom->h-(bdw.top+bdw.bottom));
137     }
138 }
139
140
141 void frame_bar_geom(const WFrame *frame, WRectangle *geom)
142 {
143     uint off;
144     
145     if(BAR_INSIDE_BORDER(frame)){
146         off=0; /*get_spacing(frame);*/
147         frame_border_inner_geom(frame, geom);
148     }else{
149         off=0;
150         geom->x=0;
151         geom->y=0;
152         geom->w=(frame->barmode==FRAME_BAR_SHAPED
153                  ? frame->bar_w
154                  : REGION_GEOM(frame).w);
155     }
156     geom->x+=off;
157     geom->y+=off;
158     geom->w=maxof(0, geom->w-2*off);
159     geom->h=BAR_H(frame);
160 }
161
162
163 void frame_managed_geom(const WFrame *frame, WRectangle *geom)
164 {
165     uint spacing=get_spacing(frame);
166     
167     frame_border_inner_geom(frame, geom);
168     
169     /*
170     geom->x+=spacing;
171     geom->y+=spacing;
172     geom->w-=2*spacing;
173     geom->h-=2*spacing;
174     */
175     
176     if(BAR_INSIDE_BORDER(frame) && BAR_EXISTS(frame)){
177         geom->y+=frame->bar_h+spacing;
178         geom->h-=frame->bar_h+spacing;
179     }
180     
181     geom->w=maxof(geom->w, 0);
182     geom->h=maxof(geom->h, 0);
183 }
184
185
186 int frame_shaded_height(const WFrame *frame)
187 {
188     if(frame->barmode==FRAME_BAR_NONE){
189         return 0;
190     }else if(!BAR_INSIDE_BORDER(frame)){
191         return frame->bar_h;
192     }else {
193         GrBorderWidths bdw;
194         
195         grbrush_get_border_widths(frame->brush, &bdw);
196         
197         return frame->bar_h+bdw.top+bdw.bottom;
198     }
199 }
200
201
202 void frame_set_shape(WFrame *frame)
203 {
204     WRectangle gs[2];
205     int n=0;
206     
207     if(frame->brush!=NULL){
208         if(BAR_EXISTS(frame)){
209             frame_bar_geom(frame, gs+n);
210             n++;
211         }
212         frame_border_geom(frame, gs+n);
213         n++;
214     
215         grbrush_set_window_shape(frame->brush, TRUE, n, gs);
216     }
217 }
218
219
220 void frame_clear_shape(WFrame *frame)
221 {
222     if(frame->brush!=NULL)
223         grbrush_set_window_shape(frame->brush, TRUE, 0, NULL);
224 }
225
226
227 #define CF_TAB_MAX_TEXT_X_OFF 10
228
229
230 static void frame_shaped_recalc_bar_size(WFrame *frame, bool complete)
231 {
232     int bar_w=0, textw=0, tmaxw=frame->tab_min_w, tmp=0;
233     WLListIterTmp itmp;
234     WRegion *sub;
235     const char *p;
236     GrBorderWidths bdw;
237     char *title;
238     uint bdtotal;
239     int i, m;
240     
241     if(frame->bar_brush==NULL)
242         return;
243     
244     m=FRAME_MCOUNT(frame);
245     
246     if(m>0){
247         grbrush_get_border_widths(frame->bar_brush, &bdw);
248         bdtotal=((m-1)*(bdw.tb_ileft+bdw.tb_iright+bdw.spacing)
249                  +bdw.right+bdw.left);
250
251         FRAME_MX_FOR_ALL(sub, frame, itmp){
252             p=region_displayname(sub);
253             if(p==NULL)
254                 continue;
255             
256             textw=grbrush_get_text_width(frame->bar_brush,
257                                          p, strlen(p));
258             if(textw>tmaxw)
259                 tmaxw=textw;
260         }
261
262         bar_w=frame->bar_max_width_q*REGION_GEOM(frame).w;
263         if(bar_w<frame->tab_min_w && 
264            REGION_GEOM(frame).w>frame->tab_min_w)
265             bar_w=frame->tab_min_w;
266         
267         tmp=bar_w-bdtotal-m*tmaxw;
268         
269         if(tmp>0){
270             /* No label truncation needed, good. See how much can be padded. */
271             tmp/=m*2;
272             if(tmp>CF_TAB_MAX_TEXT_X_OFF)
273                 tmp=CF_TAB_MAX_TEXT_X_OFF;
274             bar_w=(tmaxw+tmp*2)*m+bdtotal;
275         }else{
276             /* Some labels must be truncated */
277         }
278     }else{
279         bar_w=frame->tab_min_w;
280         if(bar_w>frame->bar_max_width_q*REGION_GEOM(frame).w)
281             bar_w=frame->bar_max_width_q*REGION_GEOM(frame).w;
282     }
283
284     if(complete || frame->bar_w!=bar_w){
285         frame->bar_w=bar_w;
286         frame_set_shape(frame);
287     }
288 }
289
290
291 static int init_title(WFrame *frame, int i)
292 {
293     int textw;
294     
295     if(frame->titles[i].text!=NULL){
296         free(frame->titles[i].text);
297         frame->titles[i].text=NULL;
298     }
299     
300     textw=frame_nth_tab_iw((WFrame*)frame, i);
301     frame->titles[i].iw=textw;
302     return textw;
303 }
304
305
306 void frame_recalc_bar(WFrame *frame, bool complete)
307 {
308     int textw, i;
309     WLListIterTmp tmp;
310     WRegion *sub;
311     char *title;
312
313     if(frame->bar_brush==NULL || frame->titles==NULL)
314         return;
315     
316     if(frame->barmode==FRAME_BAR_SHAPED)
317         frame_shaped_recalc_bar_size(frame, complete);
318     else if(complete)
319         frame_clear_shape(frame);
320     
321     i=0;
322     
323     if(FRAME_MCOUNT(frame)==0){
324         textw=init_title(frame, i);
325         if(textw>0){
326             title=grbrush_make_label(frame->bar_brush, TR("<empty frame>"), 
327                                      textw);
328             frame->titles[i].text=title;
329         }
330         return;
331     }
332     
333     FRAME_MX_FOR_ALL(sub, frame, tmp){
334         textw=init_title(frame, i);
335         if(textw>0){
336             title=region_make_label(sub, textw, frame->bar_brush);
337             frame->titles[i].text=title;
338         }
339         i++;
340     }
341 }
342
343
344 void frame_draw_bar(const WFrame *frame, bool complete)
345 {
346     WRectangle geom;
347     
348     if(frame->bar_brush==NULL
349        || !BAR_EXISTS(frame)
350        || frame->titles==NULL){
351         return;
352     }
353     
354     frame_bar_geom(frame, &geom);
355
356     grbrush_begin(frame->bar_brush, &geom, GRBRUSH_AMEND);
357     
358     grbrush_init_attr(frame->bar_brush, &frame->baseattr);
359     
360     grbrush_draw_textboxes(frame->bar_brush, &geom, frame->titles_n, 
361                            frame->titles, complete);
362     
363     grbrush_end(frame->bar_brush);
364 }
365
366 void frame_draw(const WFrame *frame, bool complete)
367 {
368     WRectangle geom;
369     
370     if(frame->brush==NULL)
371         return;
372         
373     frame_border_geom(frame, &geom);
374     
375     grbrush_begin(frame->brush, &geom, (complete ? 0 : GRBRUSH_NO_CLEAR_OK));
376     
377     grbrush_init_attr(frame->brush, &frame->baseattr);
378     
379     grbrush_draw_border(frame->brush, &geom);
380     
381     frame_draw_bar(frame, TRUE);
382     
383     grbrush_end(frame->brush);
384 }
385
386
387 void frame_brushes_updated(WFrame *frame)
388 {
389     WFrameBarMode barmode;
390     ExtlTab tab;
391     char *s;
392
393     if(frame->brush==NULL)
394         return;
395     
396     if(frame->mode==FRAME_MODE_FLOATING){
397         barmode=FRAME_BAR_SHAPED;
398     }else if(frame->mode==FRAME_MODE_TILED || frame->mode==FRAME_MODE_UNKNOWN ||
399             frame->mode==FRAME_MODE_TRANSIENT_ALT){
400         barmode=FRAME_BAR_INSIDE;
401     }else{
402         barmode=FRAME_BAR_NONE;
403     }
404     
405     if(grbrush_get_extra(frame->brush, "bar", 's', &s)){
406         if(strcmp(s, "inside")==0)
407             barmode=FRAME_BAR_INSIDE;
408         else if(strcmp(s, "outside")==0)
409             barmode=FRAME_BAR_OUTSIDE;
410         else if(strcmp(s, "shaped")==0)
411             barmode=FRAME_BAR_SHAPED;
412         else if(strcmp(s, "none")==0)
413             barmode=FRAME_BAR_NONE;
414         free(s);
415     }
416         
417     frame->barmode=barmode;
418     
419     if(barmode==FRAME_BAR_NONE || frame->bar_brush==NULL){
420         frame->bar_h=0;
421     }else{
422         GrBorderWidths bdw;
423         GrFontExtents fnte;
424
425         grbrush_get_border_widths(frame->bar_brush, &bdw);
426         grbrush_get_font_extents(frame->bar_brush, &fnte);
427
428         frame->bar_h=bdw.top+bdw.bottom+fnte.max_height;
429     }
430     
431     /* shaped mode stuff */
432     frame->tab_min_w=100;
433     frame->bar_max_width_q=0.95;
434     
435     if(grbrush_get_extra(frame->brush, "floatframe_tab_min_w",
436                          'i', &(frame->tab_min_w))){
437         if(frame->tab_min_w<=0)
438             frame->tab_min_w=1;
439     }
440     
441     if(grbrush_get_extra(frame->brush, "floatframe_bar_max_w_q", 
442                          'd', &(frame->bar_max_width_q))){
443         if(frame->bar_max_width_q<=0.0 || frame->bar_max_width_q>1.0)
444             frame->bar_max_width_q=1.0;
445     }
446 }
447
448
449 /*}}}*/
450
451
452 /*{{{ Misc. */
453
454
455 void frame_updategr(WFrame *frame)
456 {
457     frame_release_brushes(frame);
458     
459     frame_initialise_gr(frame);
460     
461     /* Update children */
462     region_updategr_default((WRegion*)frame);
463     
464     mplex_fit_managed(&frame->mplex);
465     frame_recalc_bar(frame, TRUE);
466     frame_set_background(frame, TRUE);
467 }
468
469
470 static StringIntMap frame_tab_styles[]={
471     {"tab-frame-unknown", FRAME_MODE_UNKNOWN},
472     {"tab-frame-unknown-alt", FRAME_MODE_UNKNOWN_ALT},
473     {"tab-frame-tiled", FRAME_MODE_TILED},
474     {"tab-frame-tiled-alt", FRAME_MODE_TILED_ALT},
475     {"tab-frame-floating", FRAME_MODE_FLOATING},
476     {"tab-frame-floating-alt", FRAME_MODE_FLOATING_ALT},
477     {"tab-frame-transient", FRAME_MODE_TRANSIENT},
478     {"tab-frame-transient-alt", FRAME_MODE_TRANSIENT_ALT},
479     END_STRINGINTMAP
480 };
481
482
483 const char *framemode_get_tab_style(WFrameMode mode)
484 {
485     return stringintmap_key(frame_tab_styles, mode, "tab-frame");
486 }
487
488
489 const char *framemode_get_style(WFrameMode mode)
490 {
491     const char *p=framemode_get_tab_style(mode);
492     assert(p!=NULL);
493     return (p+4);
494 }
495
496
497 void frame_initialise_gr(WFrame *frame)
498 {
499     Window win=frame->mplex.win.win;
500     WRootWin *rw=region_rootwin_of((WRegion*)frame);
501     const char *style=framemode_get_style(frame->mode);
502     const char *tab_style=framemode_get_tab_style(frame->mode);
503     
504     frame->brush=gr_get_brush(win, rw, style);
505     
506     if(frame->brush==NULL)
507         return;
508     
509     frame->bar_brush=grbrush_get_slave(frame->brush, rw, tab_style);
510     
511     if(frame->bar_brush==NULL)
512         return;
513     
514     frame_brushes_updated(frame);
515 }
516
517
518 void frame_release_brushes(WFrame *frame)
519 {
520     if(frame->bar_brush!=NULL){
521         grbrush_release(frame->bar_brush);
522         frame->bar_brush=NULL;
523     }
524     
525     if(frame->brush!=NULL){
526         grbrush_release(frame->brush);
527         frame->brush=NULL;
528     }
529 }
530
531
532 bool frame_set_background(WFrame *frame, bool set_always)
533 {
534     GrTransparency mode=GR_TRANSPARENCY_DEFAULT;
535     
536     if(FRAME_CURRENT(frame)!=NULL){
537         if(OBJ_IS(FRAME_CURRENT(frame), WClientWin)){
538             WClientWin *cwin=(WClientWin*)FRAME_CURRENT(frame);
539             mode=(cwin->flags&CLIENTWIN_PROP_TRANSPARENT
540                   ? GR_TRANSPARENCY_YES : GR_TRANSPARENCY_NO);
541         }else if(!OBJ_IS(FRAME_CURRENT(frame), WGroup)){
542             mode=GR_TRANSPARENCY_NO;
543         }
544     }
545     
546     if(mode!=frame->tr_mode || set_always){
547         frame->tr_mode=mode;
548         if(frame->brush!=NULL){
549             grbrush_enable_transparency(frame->brush, mode);
550             window_draw((WWindow*)frame, TRUE);
551         }
552         return TRUE;
553     }
554     
555     return FALSE;
556 }
557
558
559 void frame_setup_dragwin_style(WFrame *frame, GrStyleSpec *spec, int tab)
560 {
561     gr_stylespec_append(spec, &frame->baseattr);
562     gr_stylespec_append(spec, &frame->titles[tab].attr);
563 }
564
565
566 /*}}}*/
567
568
569 /*{{{ Activated/inactivated */
570
571
572 void frame_inactivated(WFrame *frame)
573 {
574     ensure_create_attrs();
575     
576     gr_stylespec_set(&frame->baseattr, GR_ATTR(inactive));
577     gr_stylespec_unset(&frame->baseattr, GR_ATTR(active));
578
579     window_draw((WWindow*)frame, FALSE);
580 }
581
582
583 void frame_activated(WFrame *frame)
584 {
585     ensure_create_attrs();
586     
587     gr_stylespec_set(&frame->baseattr, GR_ATTR(active));
588     gr_stylespec_unset(&frame->baseattr, GR_ATTR(inactive));
589     
590     window_draw((WWindow*)frame, FALSE);
591 }
592
593
594 /*}}}*/
595