]> git.decadent.org.uk Git - ion3.git/blob - mod_panews/splitext.c
[svn-inject] Installing original source of ion3
[ion3.git] / mod_panews / splitext.c
1 /*
2  * ion/panews/splitext.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2006. 
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 #include <limits.h>
14 #include <libtu/objp.h>
15 #include <libtu/minmax.h>
16 #include <ioncore/common.h>
17 #include <ioncore/global.h>
18 #include <ioncore/rootwin.h>
19 #include <ioncore/xwindow.h>
20 #include <ioncore/window.h>
21 #include <mod_tiling/split.h>
22 #include "splitext.h"
23 #include "unusedwin.h"
24
25
26 #define GEOM(X) (((WSplit*)(X))->geom)
27
28
29 /*{{{ Init/deinit */
30
31
32 bool splitunused_init(WSplitUnused *split, const WRectangle *geom,
33                       WPaneWS *ws)
34 {
35     WWindow *par=REGION_PARENT(ws);
36     WUnusedWin *uwin;
37     WFitParams fp;
38
39     assert(par!=NULL);
40     
41     fp.g=*geom;
42     fp.mode=REGION_FIT_EXACT;
43     
44     uwin=create_unusedwin(par, &fp);
45     
46     if(uwin==NULL)
47         return FALSE;
48     
49     if(!splitregion_init(&(split->regnode), geom, (WRegion*)uwin)){
50         destroy_obj((Obj*)uwin);
51         return FALSE;
52     }
53     
54     if(!tiling_managed_add(&ws->tiling, (WRegion*)uwin)){
55         split->regnode.reg=NULL;
56         destroy_obj((Obj*)uwin);
57         return FALSE;
58     }
59     
60     return TRUE;
61 }
62
63
64 WSplitUnused *create_splitunused(const WRectangle *geom, WPaneWS *ws)
65 {
66     CREATEOBJ_IMPL(WSplitUnused, splitunused, (p, geom, ws));
67 }
68
69
70 bool splitpane_init(WSplitPane *pane, const WRectangle *geom, WSplit *cnt)
71 {
72     pane->contents=cnt;
73     pane->marker=NULL;
74     
75     if(!splitinner_init(&(pane->isplit), geom))
76         return FALSE;
77
78     return TRUE;
79 }
80
81
82 WSplitPane *create_splitpane(const WRectangle *geom, WSplit *cnt)
83 {
84     CREATEOBJ_IMPL(WSplitPane, splitpane, (p, geom, cnt));
85 }
86
87
88 void splitunused_deinit(WSplitUnused *split)
89 {
90     if(split->regnode.reg!=NULL){
91         destroy_obj((Obj*)split->regnode.reg);
92         split->regnode.reg=NULL;
93     }
94     
95     splitregion_deinit(&(split->regnode));
96 }
97
98
99 void splitpane_deinit(WSplitPane *split)
100 {
101     if(split->contents!=NULL){
102         WSplit *tmp=split->contents;
103         split->contents=NULL;
104         tmp->parent=NULL;
105         destroy_obj((Obj*)tmp);
106     }
107     splitinner_deinit(&(split->isplit));
108 }
109
110
111 /*}}}*/
112
113
114 /*{{{ X window handling */
115
116
117 static void splitpane_stacking(WSplitPane *pane, 
118                                Window *bottomret, Window *topret)
119 {
120
121     *bottomret=None;
122     *topret=None;
123     
124     if(pane->contents!=NULL)
125         split_stacking(pane->contents, bottomret, topret);
126 }
127
128
129 static void splitpane_restack(WSplitPane *pane, Window other, int mode)
130 {
131     if(pane->contents!=None)
132         split_restack(pane->contents, other, mode);
133 }
134
135
136 static void stack_restack_reg(WRegion *reg, Window *other, int *mode)
137 {
138     Window b=None, t=None;
139     
140     if(reg!=NULL){
141         region_restack(reg, *other, *mode);
142         region_stacking(reg, &b, &t);
143         if(t!=None){
144             *other=t;
145             *mode=Above;
146         }
147     }
148 }
149
150
151 static void stack_restack_split(WSplit *split, Window *other, int *mode)
152 {
153     Window b=None, t=None;
154     
155     if(split!=NULL){
156         split_restack(split, *other, *mode);
157         split_stacking(split, &b, &t);
158         if(t!=None){
159             *other=t;
160             *mode=Above;
161         }
162     }
163 }
164
165
166 static void splitpane_reparent(WSplitPane *pane, WWindow *target)
167 {
168     if(pane->contents!=NULL)
169         split_reparent(pane->contents, target);
170 }
171
172
173 static void reparentreg(WRegion *reg, WWindow *target)
174 {
175     WRectangle g=REGION_GEOM(reg);
176     region_reparent(reg, target, &g, REGION_FIT_EXACT);
177 }
178
179
180 /*}}}*/
181
182
183 /*{{{ Geometry */
184
185
186 static void set_unused_bounds(WSplit *node)
187 {
188     node->min_w=0;
189     node->min_h=0;
190     node->max_w=INT_MAX;
191     node->max_h=INT_MAX;
192     node->unused_w=node->geom.w;
193     node->unused_h=node->geom.h;
194 }
195
196
197 static void copy_bounds(WSplit *dst, const WSplit *src)
198 {
199     dst->min_w=src->min_w;
200     dst->min_h=src->min_h;
201     dst->max_w=src->max_w;
202     dst->max_h=src->max_h;
203     dst->unused_w=src->unused_w;
204     dst->unused_h=src->unused_h;
205 }
206
207
208 static void splitunused_update_bounds(WSplitUnused *node, bool recursive)
209 {
210     set_unused_bounds((WSplit*)node);
211 }
212
213
214 static void splitpane_update_bounds(WSplitPane *node, bool recursive)
215 {
216     if(node->contents!=NULL){
217         if(recursive)
218             split_update_bounds(node->contents, recursive);
219         copy_bounds((WSplit*)node, node->contents);
220     }else{
221         set_unused_bounds((WSplit*)node);
222     }
223 }
224
225
226 static int infadd(int x, int y)
227 {
228     return ((x==INT_MAX || y==INT_MAX) ? INT_MAX : (x+y));
229 }
230
231
232 static void splitpane_do_resize(WSplitPane *pane, const WRectangle *ng, 
233                                 int hprimn, int vprimn, bool transpose)
234 {
235     if(transpose && pane->marker!=NULL){
236         char *growdir=strchr(pane->marker, ':');
237         if(growdir!=NULL){
238             const char *newdir=NULL;
239             growdir++;
240             
241             if(strcmp(growdir, "right")==0)
242                 newdir="down";
243             else if(strcmp(growdir, "left")==0)
244                 newdir="up";
245             if(strcmp(growdir, "down")==0)
246                 newdir="right";
247             else if(strcmp(growdir, "up")==0)
248                 newdir="left";
249             
250             if(newdir!=NULL){
251                 char *newmarker=NULL;
252                 *growdir='\0';
253                 libtu_asprintf(&newmarker, "%s:%s", pane->marker, newdir);
254                 if(newmarker==NULL){
255                     *growdir=':';
256                 }else{
257                     free(pane->marker);
258                     pane->marker=newmarker;
259                 }
260             }
261         }
262         
263     }
264     
265     ((WSplit*)pane)->geom=*ng;
266     
267     if(pane->contents!=NULL)
268         split_do_resize(pane->contents, ng, hprimn, vprimn, transpose);
269 }
270
271
272 static void splitpane_do_rqsize(WSplitPane *pane, WSplit *node, 
273                                 RootwardAmount *ha, RootwardAmount *va, 
274                                 WRectangle *rg, bool tryonly)
275 {
276     WSplitInner *par=((WSplit*)pane)->parent;
277     
278     if(par!=NULL){
279         splitinner_do_rqsize(par, (WSplit*)pane, ha, va, rg, tryonly);
280         if(!tryonly)
281             ((WSplit*)pane)->geom=*rg;
282     }else{
283         *rg=GEOM(pane);
284     }
285 }
286
287
288 /*}}}*/
289
290
291 /*{{{ Tree manipulation */
292
293
294 static void splitpane_replace(WSplitPane *pane, WSplit *child, WSplit *what)
295 {
296     assert(child==pane->contents && what!=NULL);
297     
298     child->parent=NULL;
299     pane->contents=what;
300     what->parent=(WSplitInner*)pane;
301     what->ws_if_root=NULL; /* May not be needed */
302 }
303
304
305 static WPaneWS *find_ws(WSplit *split)
306 {
307     if(split->parent!=NULL)
308         return find_ws((WSplit*)split->parent);
309     
310     if(split->ws_if_root!=NULL)
311         return OBJ_CAST(split->ws_if_root, WPaneWS);
312     
313     return NULL;
314 }
315
316
317 static void splitpane_remove(WSplitPane *pane, WSplit *child, 
318                              bool reclaim_space)
319 {
320     WSplitInner *parent=((WSplit*)pane)->parent;
321     WSplitUnused *un;
322     WPaneWS *ws=find_ws((WSplit*)pane);
323     
324     assert(child==pane->contents);
325     
326     pane->contents=NULL;
327     child->parent=NULL;
328
329     if(ws!=NULL
330        && !OBJ_IS_BEING_DESTROYED(ws)
331        && !OBJ_IS_BEING_DESTROYED(pane)){
332         pane->contents=(WSplit*)create_splitunused(&GEOM(pane), ws);
333         if(pane->contents!=NULL){
334             pane->contents->parent=(WSplitInner*)pane;
335             return;
336         }
337     }
338     
339     if(parent!=NULL)
340         splitinner_remove(parent, (WSplit*)pane, reclaim_space);
341     else
342         splittree_changeroot((WSplit*)pane, NULL);
343     
344     destroy_obj((Obj*)pane);
345 }
346
347
348 /*}}}*/
349
350
351 /*{{{ Tree traversal */
352
353
354 static bool filter_any(WSplit *split)
355 {
356     return OBJ_IS(split, WSplitRegion);
357 }
358
359
360 static bool filter_no_unused(WSplit *split)
361 {
362     return (OBJ_IS(split, WSplitRegion)
363             && !OBJ_IS(split, WSplitUnused));
364 }
365
366
367 static bool filter_no_stdisp(WSplit *split)
368 {
369     return (OBJ_IS(split, WSplitRegion)
370             && !OBJ_IS(split, WSplitST));
371 }
372
373
374 static bool filter_no_stdisp_unused(WSplit *split)
375 {
376     return (OBJ_IS(split, WSplitRegion)
377             && !OBJ_IS(split, WSplitST)
378             && !OBJ_IS(split, WSplitUnused));
379             
380 }
381
382
383 static WSplit *splitpane_current_todir(WSplitPane *pane, int dir, int primn,
384                                        WSplitFilter *filter)
385 {
386     WSplit *ret=NULL;
387     
388     if(pane->contents==NULL)
389         return NULL;
390     
391     /* Try non-unused first */
392     if(filter==filter_no_stdisp){
393         ret=split_current_todir(pane->contents, dir, primn, 
394                                 filter_no_stdisp_unused);
395     }else if(filter==filter_any){
396         ret=split_current_todir(pane->contents, dir, primn, 
397                                 filter_no_unused);
398     }
399     
400     if(ret==NULL)
401         ret=split_current_todir(pane->contents, dir, primn, filter);
402     
403     return ret;
404 }
405
406
407 static void splitpane_forall(WSplitPane *pane, WSplitFn *fn)
408 {
409     if(pane->contents!=NULL)
410         fn(pane->contents);
411 }
412
413
414 static WSplit *splitpane_current(WSplitPane *pane)
415 {
416     return pane->contents;
417 }
418
419
420 static WSplitRegion *get_node_check(WPaneWS *ws, WRegion *reg)
421 {
422     WSplitRegion *node;
423
424     if(reg==NULL)
425         return NULL;
426     
427     node=splittree_node_of(reg);
428     
429     if(node==NULL || REGION_MANAGER(reg)!=(WRegion*)ws)
430         return NULL;
431     
432     return node;
433 }
434
435
436 static WSplitRegion *do_get_nextto(WSplit *node, int dir, int primn, 
437                                    bool any, bool paneonly)
438 {
439     WSplitFilter *filter=(any ? filter_no_unused : filter_no_stdisp_unused);
440     WSplit *nextto=NULL;
441
442     while(node->parent!=NULL){
443         if(OBJ_IS(node, WSplitPane)){
444             if(paneonly)
445                 break;
446             filter=(any ? filter_any : filter_no_stdisp);
447         }
448         nextto=splitinner_nextto(node->parent, node, dir, primn, filter);
449         if(nextto!=NULL)
450             break;
451         node=(WSplit*)(node->parent);
452     }
453     
454     if(OBJ_IS(nextto, WSplitRegion))
455         return (WSplitRegion*)nextto;
456     return NULL;
457 }
458
459
460 WRegion *panews_do_get_nextto(WPaneWS *ws, WRegion *reg,
461                               int dir, int primn, bool any)
462 {
463     WSplitRegion *node=get_node_check(ws, reg), *nextto=NULL;
464
465     if(node==NULL)
466         return NULL;
467     
468     nextto=do_get_nextto((WSplit*)node, dir, primn, TRUE, FALSE);
469     
470     if(nextto!=NULL)
471         return nextto->reg;
472
473     return NULL;
474 }
475
476 WRegion *panews_do_get_farthest(WPaneWS *ws,
477                                 int dir, int primn, bool any)
478 {
479     WSplitFilter *filter=(any ? filter_any : filter_no_stdisp);
480     WSplit *node=NULL;
481     if(ws->tiling.split_tree!=NULL)
482         node=split_current_todir(ws->tiling.split_tree, dir, primn, filter);
483     if(node!=NULL && OBJ_IS(node, WSplitRegion))
484         return ((WSplitRegion*)node)->reg;
485     return NULL;
486 }
487
488
489 WSplitRegion *split_tree_find_region_in_pane_of(WSplit *node)
490 {
491     return do_get_nextto(node, SPLIT_ANY, PRIMN_ANY, FALSE, TRUE);
492 }
493
494
495 /*}}}*/
496
497
498 /*{{{ Markers and other exports */
499
500
501 /*EXTL_DOC
502  * Get marker.
503  */
504 EXTL_SAFE
505 EXTL_EXPORT_MEMBER
506 const char *splitpane_marker(WSplitPane *pane)
507 {
508     return pane->marker;
509 }
510
511
512 /*EXTL_DOC
513  * Set marker.
514  */
515 EXTL_EXPORT_MEMBER
516 bool splitpane_set_marker(WSplitPane *pane, const char *s)
517 {
518     char *s2=NULL;
519     
520     if(s!=NULL){
521         s2=scopy(s);
522         if(s2==NULL)
523             return FALSE;
524     }
525     
526     if(pane->marker==NULL)
527         free(pane->marker);
528     
529     pane->marker=s2;
530     
531     return TRUE;
532 }
533
534
535 /*EXTL_DOC
536  * Get root of contained sub-split tree.
537  */
538 EXTL_SAFE
539 EXTL_EXPORT_MEMBER
540 WSplit *splitpane_contents(WSplitPane *pane)
541 {
542     return pane->contents;
543 }
544
545
546 /*}}}*/
547
548
549 /*{{{ Save support */
550
551
552 static bool splitunused_get_config(WSplitUnused *node, ExtlTab *ret)
553 {
554     *ret=split_base_config((WSplit*)node);
555     return TRUE;
556 }
557
558
559 static bool splitpane_get_config(WSplitPane *pane, ExtlTab *ret)
560 {
561     *ret=split_base_config((WSplit*)pane);
562     
563     if(pane->contents!=NULL){
564         ExtlTab t;
565         if(!split_get_config(pane->contents, &t)){
566             extl_unref_table(*ret);
567             return FALSE;
568         }
569         extl_table_sets_t(*ret, "contents", t);
570         extl_unref_table(t);
571     }
572     
573     extl_table_sets_s(*ret, "marker", pane->marker);
574
575     return TRUE;
576 }
577
578
579 /*}}}*/
580
581
582 /*{{{ The classes */
583
584
585 static DynFunTab splitunused_dynfuntab[]={
586     {split_update_bounds, splitunused_update_bounds},
587     {(DynFun*)split_get_config, (DynFun*)splitunused_get_config},
588     END_DYNFUNTAB,
589 };
590
591
592 static DynFunTab splitpane_dynfuntab[]={
593     {split_update_bounds, splitpane_update_bounds},
594     {split_do_resize, splitpane_do_resize},
595     {splitinner_do_rqsize, splitpane_do_rqsize},
596     {splitinner_replace, splitpane_replace},
597     {splitinner_remove, splitpane_remove},
598     {(DynFun*)split_current_todir, (DynFun*)splitpane_current_todir},
599     {(DynFun*)splitinner_current, (DynFun*)splitpane_current},
600     {(DynFun*)split_get_config, (DynFun*)splitpane_get_config},
601     {splitinner_forall, splitpane_forall},
602     {split_stacking, splitpane_stacking},
603     {split_restack, splitpane_restack},
604     {split_reparent, splitpane_reparent},
605     END_DYNFUNTAB,
606 };
607
608
609 EXTL_EXPORT
610 IMPLCLASS(WSplitUnused, WSplitRegion, splitunused_deinit, splitunused_dynfuntab);
611
612 EXTL_EXPORT
613 IMPLCLASS(WSplitPane, WSplitInner, splitpane_deinit, splitpane_dynfuntab);
614
615
616 /*}}}*/
617