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