]> git.decadent.org.uk Git - ion3.git/blob - ioncore/mplexpholder.c
478ac2dd61e2152b5eb3a3cf88bed0af81fbb680
[ion3.git] / ioncore / mplexpholder.c
1 /*
2  * ion/ioncore/mplexpholder.c
3  *
4  * Copyright (c) Tuomo Valkonen 2005-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 <libtu/objp.h>
13 #include <libtu/obj.h>
14 #include <libtu/pointer.h>
15
16 #include "common.h"
17 #include "mplex.h"
18 #include "mplexpholder.h"
19 #include "llist.h"
20 #include "framedpholder.h"
21
22
23 static void mplex_watch_handler(Watch *watch, Obj *mplex);
24
25
26 /*{{{ Primitives */
27
28
29 static bool on_ph_list(WMPlexPHolder *ll, WMPlexPHolder *ph)
30 {
31     return ph->prev!=NULL;
32 }
33
34
35 static void mplexpholder_do_link(WMPlexPHolder *ph, 
36                                  WMPlex *mplex,
37                                  WMPlexPHolder *after,
38                                  WLListNode *or_after)
39 {
40     assert(mplex==(WMPlex*)ph->mplex_watch.obj && mplex!=NULL);
41     
42     if(after!=NULL){
43         assert(after->after==or_after);
44         
45         if(after->after!=NULL){
46             LINK_ITEM_AFTER(after->after->phs, after, ph, next, prev);
47         }else{
48             assert(on_ph_list(mplex->mx_phs, after));
49             LINK_ITEM_AFTER(mplex->mx_phs, after, ph, next, prev);
50         }
51         ph->after=after->after;
52     }else if(or_after!=NULL){
53         LINK_ITEM_FIRST(or_after->phs, ph, next, prev);
54         ph->after=or_after;
55     }else{
56         LINK_ITEM_FIRST(mplex->mx_phs, ph, next, prev);
57         ph->after=NULL;
58     }
59 }
60
61
62 void mplexpholder_do_unlink(WMPlexPHolder *ph, WMPlex *mplex)
63 {
64     if(ph->recreate_pholder!=NULL){
65         if(ph->prev!=NULL)
66             ph->prev->recreate_pholder=ph->recreate_pholder;
67         else
68             destroy_obj((Obj*)ph->recreate_pholder);
69         ph->recreate_pholder=NULL;
70     }
71     
72     if(ph->after!=NULL){
73         UNLINK_ITEM(ph->after->phs, ph, next, prev);
74     }else if(mplex!=NULL && on_ph_list(mplex->mx_phs, ph)){
75         UNLINK_ITEM(mplex->mx_phs, ph, next, prev);
76     }else{
77         WMPlexPHolder *next=ph->next;
78     
79         assert((ph->next==NULL && ph->prev==NULL)
80                || ph->mplex_watch.obj==NULL);
81         
82         if(ph->next!=NULL)
83             ph->next->prev=ph->prev;
84         if(ph->prev!=NULL)
85             ph->prev->next=next;
86     }
87     
88     ph->after=NULL;
89     ph->next=NULL;
90     ph->prev=NULL;
91 }
92
93
94 /*}}}*/
95
96
97 /*{{{ Init/deinit */
98
99
100 static void mplex_watch_handler(Watch *watch, Obj *mplex)
101 {
102     WMPlexPHolder *ph=FIELD_TO_STRUCT(WMPlexPHolder, mplex_watch, watch);
103     mplexpholder_do_unlink(ph, (WMPlex*)mplex);
104     pholder_redirect(&(ph->ph), (WRegion*)mplex);
105 }
106
107
108 static WMPlexAttachParams dummy_param={0, 0, {0, 0, 0, 0}, 0, 0};
109
110
111 bool mplexpholder_init(WMPlexPHolder *ph, WMPlex *mplex, WStacking *st,
112                        WMPlexAttachParams *param)
113 {
114     pholder_init(&(ph->ph));
115
116     watch_init(&(ph->mplex_watch));
117     ph->after=NULL;
118     ph->next=NULL;
119     ph->prev=NULL;
120     ph->param.flags=0;
121     ph->recreate_pholder=NULL;
122     
123     if(!watch_setup(&(ph->mplex_watch), (Obj*)mplex, mplex_watch_handler)){
124         pholder_deinit(&(ph->ph));
125         return FALSE;
126     }
127
128     if(param==NULL)
129         param=&dummy_param;
130     
131     if(st!=NULL){
132         if(st->lnode!=NULL)
133             mplexpholder_do_link(ph, mplex, 
134                                  LIST_LAST(st->lnode->phs, next, prev), 
135                                  st->lnode);
136         else
137             ph->param.flags|=MPLEX_ATTACH_UNNUMBERED;
138         
139         ph->param.flags|=(MPLEX_ATTACH_SIZEPOLICY|
140                           MPLEX_ATTACH_GEOM|
141                           MPLEX_ATTACH_LEVEL|
142                           (st->hidden ? MPLEX_ATTACH_HIDDEN : 0));
143         ph->param.szplcy=st->szplcy;
144         ph->param.geom=REGION_GEOM(st->reg);
145         ph->param.level=st->level;
146     }else{
147         ph->param=*param;
148         
149         if(!(param->flags&MPLEX_ATTACH_UNNUMBERED)){
150             int index=(param->flags&MPLEX_ATTACH_INDEX
151                        ? param->index
152                        : mplex_default_index(mplex));
153             WLListNode *or_after=llist_index_to_after(mplex->mx_list, 
154                                                       mplex->mx_current, 
155                                                       index);
156             WMPlexPHolder *after=(index==LLIST_INDEX_LAST
157                                   ? (or_after!=NULL
158                                      ? LIST_LAST(or_after->phs, next, prev) 
159                                      : LIST_LAST(mplex->mx_phs, next, prev))
160                                   : NULL);
161
162             mplexpholder_do_link(ph, mplex, after, or_after);
163         }
164     }
165     
166     return TRUE;
167 }
168  
169
170 WMPlexPHolder *create_mplexpholder(WMPlex *mplex,
171                                    WStacking *st,
172                                    WMPlexAttachParams *param)
173 {
174     CREATEOBJ_IMPL(WMPlexPHolder, mplexpholder, (p, mplex, st, param));
175 }
176
177
178 void mplexpholder_deinit(WMPlexPHolder *ph)
179 {
180     mplexpholder_do_unlink(ph, (WMPlex*)ph->mplex_watch.obj);
181     watch_reset(&(ph->mplex_watch));
182     pholder_deinit(&(ph->ph));
183 }
184
185
186 /*}}}*/
187
188
189 /*{{{ Move, attach, layer */
190
191
192 typedef struct{
193     WMPlexPHolder *ph, *ph_head;
194     WRegionAttachData *data;
195     WFramedParam *param;
196 } RP;
197
198
199 WRegion *recreate_handler(WWindow *par, 
200                           const WFitParams *fp, 
201                           void *rp_)
202 {
203     RP *rp=(RP*)rp_;
204     WMPlexPHolder *ph=rp->ph, *ph_head=rp->ph_head, *phtmp;
205     WFramedParam *param=rp->param;
206     WFrame *frame;
207     WRegion *reg;
208     
209     frame=create_frame(par, fp, param->mode);
210     
211     if(frame==NULL)
212         return NULL;
213     
214     /* Move pholders to frame */
215     frame->mplex.mx_phs=ph_head;
216     
217     for(phtmp=frame->mplex.mx_phs; phtmp!=NULL; phtmp=phtmp->next)
218         watch_setup(&(phtmp->mplex_watch), (Obj*)frame, mplex_watch_handler);
219         
220     /* Attach */
221     if(fp->mode&(REGION_FIT_BOUNDS|REGION_FIT_WHATEVER))
222         ph->param.flags|=MPLEX_ATTACH_WHATEVER;
223     
224     reg=mplex_do_attach_pholder(&frame->mplex, ph, rp->data);
225
226     ph->param.flags&=~MPLEX_ATTACH_WHATEVER;
227
228     if(reg==NULL){
229         /* Try to recover */
230         for(phtmp=frame->mplex.mx_phs; phtmp!=NULL; phtmp=phtmp->next)
231             watch_reset(&(phtmp->mplex_watch));
232         frame->mplex.mx_phs=NULL;
233     
234         destroy_obj((Obj*)frame);
235         
236         return NULL;
237     }else{
238         frame_adjust_to_initial(frame, fp, param, reg);
239         
240         return (WRegion*)frame;
241     }
242 }
243
244
245 static WMPlexPHolder *get_head(WMPlexPHolder *ph)
246 {
247     while(1){
248         /* ph->prev==NULL should not happen.. */
249         if(ph->prev==NULL || ph->prev->next==NULL)
250             break;
251         ph=ph->prev;
252     }
253     
254     return ph;
255 }
256
257
258 static WFramedPHolder *get_recreate_ph(WMPlexPHolder *ph)
259 {
260     return get_head(ph)->recreate_pholder;
261 }
262
263     
264 static WRegion *mplexpholder_attach_recreate(WMPlexPHolder *ph, int flags,
265                                              WRegionAttachData *data)
266 {
267     WRegionAttachData data2;
268     WFramedPHolder *fph;
269     WPHolder *root;
270     WRegion *reg;
271     RP rp;
272     
273     rp.ph_head=get_head(ph);
274     
275     assert(rp.ph_head!=NULL);
276     
277     fph=rp.ph_head->recreate_pholder;
278     
279     if(fph==NULL)
280         return NULL;
281     
282     rp.ph=ph;
283     rp.data=data;
284     rp.param=&fph->param;
285     
286     data2.type=REGION_ATTACH_NEW;
287     data2.u.n.fn=recreate_handler;
288     data2.u.n.param=&rp;
289     
290     reg=pholder_do_attach(fph->cont, flags, &data2); /* == frame */
291     
292     if(reg!=NULL){
293         destroy_obj((Obj*)fph);
294         rp.ph_head->recreate_pholder=NULL;
295     }
296
297     return reg;
298 }
299
300
301 WRegion *mplexpholder_do_attach(WMPlexPHolder *ph, int flags,
302                                 WRegionAttachData *data)
303 {
304     WMPlex *mplex=(WMPlex*)ph->mplex_watch.obj;
305     
306     if(mplex==NULL)
307         return mplexpholder_attach_recreate(ph, flags, data);
308     
309     if(flags&PHOLDER_ATTACH_SWITCHTO)
310         ph->param.flags|=MPLEX_ATTACH_SWITCHTO;
311     else
312         ph->param.flags&=~MPLEX_ATTACH_SWITCHTO;
313     
314     return mplex_do_attach_pholder(mplex, ph, data);
315 }
316
317
318 bool mplexpholder_move(WMPlexPHolder *ph, WMPlex *mplex, 
319                        WMPlexPHolder *after,
320                        WLListNode *or_after)
321
322 {
323     mplexpholder_do_unlink(ph, (WMPlex*)ph->mplex_watch.obj);
324
325     watch_reset(&(ph->mplex_watch));
326     
327     if(mplex==NULL)
328         return TRUE;
329     
330     if(!watch_setup(&(ph->mplex_watch), (Obj*)mplex, mplex_watch_handler))
331         return FALSE;
332
333     mplexpholder_do_link(ph, mplex, after, or_after);
334     
335     return TRUE;
336 }
337
338
339 bool mplexpholder_do_goto(WMPlexPHolder *ph)
340 {
341     WRegion *reg=(WRegion*)ph->mplex_watch.obj;
342     
343     if(reg!=NULL){
344         return region_goto(reg);
345     }else{
346         WFramedPHolder *fph=get_recreate_ph(ph);
347         
348         return (fph!=NULL
349                 ? pholder_do_goto((WPHolder*)fph)
350                 : FALSE);
351     }
352 }
353
354
355 WRegion *mplexpholder_do_target(WMPlexPHolder *ph)
356 {
357     WRegion *reg=(WRegion*)ph->mplex_watch.obj;
358     
359     if(reg!=NULL){
360         return reg;
361     }else{
362         WFramedPHolder *fph=get_recreate_ph(ph);
363         
364         return (fph!=NULL
365                 ? pholder_do_target((WPHolder*)fph)
366                 : NULL);
367     }
368 }
369
370
371 WPHolder *mplexpholder_do_root(WMPlexPHolder *ph)
372 {
373     WRegion *reg=(WRegion*)ph->mplex_watch.obj;
374     
375     if(reg!=NULL){
376         return &ph->ph;
377     }else{
378         WFramedPHolder *fph=get_recreate_ph(ph);
379         WPHolder *root;
380         
381         if(fph==NULL)
382             return NULL;
383     
384         root=pholder_root((WPHolder*)fph);
385     
386         return (root!=(WPHolder*)fph
387                 ? root
388                 : &ph->ph);
389     }
390 }
391
392
393 /*}}}*/
394
395
396 /*{{{ WMPlex routines */
397
398
399 void mplex_move_phs(WMPlex *mplex, WLListNode *node,
400                     WMPlexPHolder *after,
401                     WLListNode *or_after)
402 {
403     WMPlexPHolder *ph;
404     
405     assert(node!=or_after);
406     
407     while(node->phs!=NULL){
408         ph=node->phs;
409         
410         mplexpholder_do_unlink(ph, mplex);
411         mplexpholder_do_link(ph, mplex, after, or_after);
412         
413         after=ph;
414     }
415 }
416
417 void mplex_move_phs_before(WMPlex *mplex, WLListNode *node)
418 {
419     WMPlexPHolder *after=NULL;
420     WLListNode *or_after;
421     
422     or_after=LIST_PREV(mplex->mx_list, node, next, prev);
423                          
424     after=(or_after!=NULL
425            ? LIST_LAST(or_after->phs, next, prev)
426            : LIST_LAST(mplex->mx_phs, next, prev));
427         
428     mplex_move_phs(mplex, node, after, or_after);
429 }
430
431
432 WMPlexPHolder *mplex_managed_get_pholder(WMPlex *mplex, WRegion *mgd)
433 {
434     WStacking *st=mplex_find_stacking(mplex, mgd);
435     
436     if(st==NULL)
437         return NULL;
438     else
439         return create_mplexpholder(mplex, st, NULL);
440 }
441
442
443 WMPlexPHolder *mplex_get_rescue_pholder_for(WMPlex *mplex, WRegion *mgd)
444 {
445     WStacking *st=mplex_find_stacking(mplex, mgd);
446     
447     return create_mplexpholder(mplex, st, NULL);
448 }
449
450
451 /*}}}*/
452
453
454 /*{{{ Class information */
455
456
457 static DynFunTab mplexpholder_dynfuntab[]={
458     {(DynFun*)pholder_do_attach, 
459      (DynFun*)mplexpholder_do_attach},
460     
461     {(DynFun*)pholder_do_goto, 
462      (DynFun*)mplexpholder_do_goto},
463
464     {(DynFun*)pholder_do_target, 
465      (DynFun*)mplexpholder_do_target},
466      
467     {(DynFun*)pholder_do_root, 
468      (DynFun*)mplexpholder_do_root},
469
470     END_DYNFUNTAB
471 };
472
473
474 IMPLCLASS(WMPlexPHolder, WPHolder, mplexpholder_deinit, 
475           mplexpholder_dynfuntab);
476
477
478 /*}}}*/
479