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