4 * Copyright (c) Tuomo Valkonen 1999-2002.
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.
17 #include <libtu/minmax.h>
18 #include <libtu/objp.h>
22 #include "clientwin.h"
26 #include <libextl/extl.h>
29 /*{{{ Implementation */
32 WNamespace ioncore_internal_ns
={NULL
, FALSE
};
33 WNamespace ioncore_clientwin_ns
={NULL
, FALSE
};
36 static bool initialise_ns(WNamespace
*ns
)
44 ns
->initialised
=(ns
->rb
!=NULL
);
46 return ns
->initialised
;
50 static int parseinst(const char *name
, const char **startinst
)
52 const char *p2
, *p3
=NULL
;
61 p2
=strrchr(name
, '<');
65 inst
=strtoul(p2
+1, (char**)&p3
, 10);
67 if(inst
<0 || p3
!=name
+l
-1)
75 static int parseinst_simple(const char *inststr
)
84 inst
=strtoul(inststr
+1, (char**)&end
, 10);
86 if(inst
>=0 && end
!=NULL
&& *end
=='>')
90 warn(TR("Corrupt instance number %s."), inststr
);
95 #define COMPARE_FN ((Rb_compfn*)compare_nameinfos)
97 static int compare_nameinfos(const WRegionNameInfo
*ni1
,
98 const WRegionNameInfo
*ni2
)
104 /* Handle unnamed regions. */
109 return (ni1
==ni2
? 0 : (ni1
<ni2
? -1 : 1));
110 }else if(ni2
->name
==NULL
){
114 /* Special case: inst_off<0 means that -inst_off-1 is the actual
115 * instance number and the name does not contain it.
121 l1
=strlen(ni1
->name
);
126 l2
=strlen(ni2
->name
);
128 /* Check name part first */
130 mc
=strncmp(ni1
->name
, ni2
->name
, minof(l1
, l2
));
136 return (l1
<l2
? -1 : 1);
138 /* Same name, different instance */
141 i1
=parseinst_simple(ni1
->name
+ni1
->inst_off
);
146 i2
=parseinst_simple(ni2
->name
+ni2
->inst_off
);
151 return (i1
<i2
? -1 : 1);
153 /* Same name and instance */
159 static bool separated(const WRegionNameInfo
*ni1
,
160 const WRegionNameInfo
*ni2
, int *i1ret
)
166 assert(ni1
->name
!=NULL
);
169 /* Shouldn't happen the way this function is called below; unnamed
170 * regions are before others in the traversal order of the tree.
172 *i1ret
=parseinst_simple(ni1
->name
+ni1
->inst_off
);
176 assert(ni1
->inst_off
>=0 && ni2
->inst_off
>=0);
181 i1
=parseinst_simple(ni1
->name
+ni1
->inst_off
);
187 if(memcmp(ni1
->name
, ni2
->name
, l1
)!=0)
190 i2
=parseinst_simple(ni2
->name
+ni2
->inst_off
);
197 void region_do_unuse_name(WRegion
*reg
, bool insert_unnamed
)
199 WNamespace
*ns
=reg
->ni
.namespaceinfo
;
203 if(ns
==NULL
|| !ns
->initialised
)
206 node
=rb_find_gkey_n(ns
->rb
, &(reg
->ni
), COMPARE_FN
, &found
);
209 /*fprintf(stderr, "%s<-%p|%p->%s\n", reg->ni.name, reg,
210 rb_val(node), ((WRegion*)rb_val(node))->ni.name);*/
211 assert((rb_val(node
)==(void*)reg
));
212 rb_delete_node(node
);
215 if(reg
->ni
.name
!=NULL
){
222 if(rb_insertg(ns
->rb
, &(reg
->ni
), reg
, COMPARE_FN
))
226 reg
->ni
.namespaceinfo
=NULL
;
230 void region_unuse_name(WRegion
*reg
)
232 region_do_unuse_name(reg
, TRUE
);
233 region_notify_change(reg
);
237 static bool make_full_name(WRegionNameInfo
*ni
, const char *name
, int inst
,
240 assert(ni
->name
==NULL
);
242 if(inst
==0 && !append_always
)
243 ni
->name
=scopy(name
);
245 libtu_asprintf(&(ni
->name
), "%s<%d>", name
, inst
);
250 ni
->inst_off
=strlen(name
);
256 static bool do_use_name(WRegion
*reg
, WNamespace
*ns
, const char *name
,
257 int instrq
, bool failchange
)
260 WRegionNameInfo ni
={NULL
, 0, NULL
};
261 const char *dummy
=NULL
;
266 parsed_inst
=parseinst(name
, &dummy
);
271 /* If there's something that looks like an instance at the end of
272 * name, we will append the instance number.
274 if(parsed_inst
>=0 && inst
==0 && failchange
)
278 WRegionNameInfo tmpni
;
279 tmpni
.name
=(char*)name
;
280 tmpni
.inst_off
=-instrq
-1;
281 node
=rb_find_gkey_n(ns
->rb
, &tmpni
, COMPARE_FN
, &found
);
283 if(rb_val(node
)==(void*)reg
){
284 /* The region already has the requested name */
295 WRegionNameInfo tmpni
;
300 tmpni
.name
=(char*)name
;
302 node
=rb_find_gkey_n(ns
->rb
, &tmpni
, COMPARE_FN
, &found
);
306 Rb_node next
=rb_next(node
);
308 if(rb_val(node
)==(void*)reg
){
309 /* The region already has a name of requested form */
313 if(next
==rb_nil(ns
->rb
) ||
314 separated((const WRegionNameInfo
*)node
->k
.key
,
315 (const WRegionNameInfo
*)next
->k
.key
, &inst
)){
316 /* 'inst' should be next free instance after increment
317 * as separation was greater then one.
323 /* 'inst' should be instance of next after increment
324 * as separation was one.
332 if(!make_full_name(&ni
, name
, inst
, parsed_inst
>=0))
335 region_do_unuse_name(reg
, FALSE
);
337 reg
->ni
.name
=ni
.name
;
338 reg
->ni
.inst_off
=ni
.inst_off
;
339 reg
->ni
.namespaceinfo
=ns
;
341 if(!rb_insertg(ns
->rb
, &(reg
->ni
), reg
, COMPARE_FN
)){
345 rb_insertg(ns
->rb
, &(reg
->ni
), reg
, COMPARE_FN
);
353 static bool use_name_anyinst(WRegion
*reg
, WNamespace
*ns
, const char *name
)
355 return do_use_name(reg
, ns
, name
, -1, FALSE
);
359 static bool use_name_exact(WRegion
*reg
, WNamespace
*ns
, const char *name
)
361 return do_use_name(reg
, ns
, name
, 0, TRUE
);
365 static bool use_name_parseany(WRegion
*reg
, WNamespace
*ns
, const char *name
)
368 const char *startinst
;
372 inst
=parseinst(name
, &startinst
);
374 int realnamelen
=startinst
-name
;
375 char *realname
=ALLOC_N(char, realnamelen
+1);
378 memcpy(realname
, name
, realnamelen
);
379 realname
[realnamelen
]='\0';
380 retval
=do_use_name(reg
, ns
, realname
, inst
, FALSE
);
386 return do_use_name(reg
, ns
, name
, 0, FALSE
);
398 * Returns the name for \var{reg}.
401 const char *region_name(WRegion
*reg
)
407 char *region_make_label(WRegion
*reg
, int maxw
, GrBrush
*brush
)
409 if(reg
->ni
.name
==NULL
)
412 return grbrush_make_label(brush
, reg
->ni
.name
, maxw
);
416 static bool do_set_name(bool (*fn
)(WRegion
*reg
, WNamespace
*ns
, const char *p
),
417 WRegion
*reg
, WNamespace
*ns
, const char *p
)
429 if(nm
==NULL
|| *nm
=='\0'){
430 region_do_unuse_name(reg
, TRUE
);
432 if(!initialise_ns(ns
))
440 region_notify_change(reg
);
446 bool region_init_name(WRegion
*reg
)
448 if(OBJ_IS(reg
, WClientWin
)){
449 if(!initialise_ns(&ioncore_clientwin_ns
))
451 assert(reg
->ni
.name
==NULL
);
452 reg
->ni
.namespaceinfo
=&ioncore_clientwin_ns
;
453 return (rb_insertg(ioncore_clientwin_ns
.rb
, &(reg
->ni
), reg
,
458 if(!initialise_ns(&ioncore_internal_ns
))
461 return use_name_anyinst(reg
, &ioncore_internal_ns
, OBJ_TYPESTR(reg
));
466 * Set the name of \var{reg} to \var{p}. If the name is already in use,
467 * an instance number suffix \code{<n>} will be attempted. If \var{p} has
468 * such a suffix, it will be modified, otherwise such a suffix will be
469 * added. Setting \var{p} to nil will cause current name to be removed.
472 bool region_set_name(WRegion
*reg
, const char *p
)
474 return do_set_name(use_name_parseany
, reg
, &ioncore_internal_ns
, p
);
479 * Similar to \fnref{WRegion.set_name} except if the name is already in use,
480 * other instance numbers will not be attempted. The string \var{p} should
481 * not contain a \code{<n>} suffix or this function will fail.
484 bool region_set_name_exact(WRegion
*reg
, const char *p
)
486 return do_set_name(use_name_exact
, reg
, &ioncore_internal_ns
, p
);
490 bool clientwin_set_name(WClientWin
*cwin
, const char *p
)
492 return do_set_name(use_name_anyinst
, (WRegion
*)cwin
,
493 &ioncore_clientwin_ns
, p
);
500 /*{{{ Lookup and list */
503 static WRegion
*do_lookup_region(WNamespace
*ns
, const char *cname
,
509 const char *instptr
=NULL
;
511 if(cname
==NULL
|| !ns
->initialised
)
514 parseinst(cname
, &instptr
);
515 assert(instptr
!=NULL
);
517 ni
.name
=(char*)cname
;
518 ni
.inst_off
=instptr
-cname
;
520 node
=rb_find_gkey_n(ns
->rb
, &ni
, COMPARE_FN
, &found
);
525 return (WRegion
*)node
->v
.val
;
530 * Attempt to find a non-client window region with name \var{name} and type
531 * inheriting \var{typenam}.
534 WRegion
*ioncore_lookup_region(const char *name
, const char *typenam
)
536 return do_lookup_region(&ioncore_internal_ns
, name
, typenam
);
541 * Attempt to find a client window with name \var{name}.
544 WClientWin
*ioncore_lookup_clientwin(const char *name
)
546 return (WClientWin
*)do_lookup_region(&ioncore_clientwin_ns
, name
,
551 static ExtlTab
do_list(WNamespace
*ns
, const char *typenam
)
558 return extl_table_none();
560 tab
=extl_create_table();
562 rb_traverse(node
, ns
->rb
){
563 WRegion
*reg
=(WRegion
*)node
->v
.val
;
567 if(typenam
!=NULL
&& !obj_is_str((Obj
*)reg
, typenam
))
570 if(extl_table_seti_o(tab
, n
+1, (Obj
*)reg
))
579 * Find all non-client window regions inheriting \var{typenam}.
582 ExtlTab
ioncore_region_list(const char *typenam
)
584 return do_list(&ioncore_internal_ns
, typenam
);
589 * Return a list of all client windows.
592 ExtlTab
ioncore_clientwin_list()
594 return do_list(&ioncore_clientwin_ns
, NULL
);