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