4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
14 #include <libtu/minmax.h>
15 #include <libtu/objp.h>
16 #include <libextl/extl.h>
21 #include "clientwin.h"
27 /*{{{ Implementation */
30 WNamespace ioncore_internal_ns
={NULL
, FALSE
};
31 WNamespace ioncore_clientwin_ns
={NULL
, FALSE
};
34 static bool initialise_ns(WNamespace
*ns
)
42 ns
->initialised
=(ns
->rb
!=NULL
);
44 return ns
->initialised
;
48 static int parseinst(const char *name
, const char **startinst
)
50 const char *p2
, *p3
=NULL
;
59 p2
=strrchr(name
, '<');
63 inst
=strtoul(p2
+1, (char**)&p3
, 10);
65 if(inst
<0 || p3
!=name
+l
-1)
73 static int parseinst_simple(const char *inststr
)
82 inst
=strtoul(inststr
+1, (char**)&end
, 10);
84 if(inst
>=0 && end
!=NULL
&& *end
=='>')
88 warn(TR("Corrupt instance number %s."), inststr
);
93 #define COMPARE_FN ((Rb_compfn*)compare_nameinfos)
95 static int compare_nameinfos(const WRegionNameInfo
*ni1
,
96 const WRegionNameInfo
*ni2
)
102 /* Handle unnamed regions. */
107 return (ni1
==ni2
? 0 : (ni1
<ni2
? -1 : 1));
108 }else if(ni2
->name
==NULL
){
112 /* Special case: inst_off<0 means that -inst_off-1 is the actual
113 * instance number and the name does not contain it.
119 l1
=strlen(ni1
->name
);
124 l2
=strlen(ni2
->name
);
126 /* Check name part first */
128 mc
=strncmp(ni1
->name
, ni2
->name
, minof(l1
, l2
));
134 return (l1
<l2
? -1 : 1);
136 /* Same name, different instance */
139 i1
=parseinst_simple(ni1
->name
+ni1
->inst_off
);
141 i1
=-ni1
->inst_off
-1; /*???*/
144 i2
=parseinst_simple(ni2
->name
+ni2
->inst_off
);
146 i2
=-ni2
->inst_off
-1; /*???*/
149 return (i1
<i2
? -1 : 1);
151 /* Same name and instance */
156 static bool insert_reg(Rb_node tree
, WRegion
*reg
)
158 assert(reg
->ni
.node
==NULL
);
159 reg
->ni
.node
=(void*)rb_insertg(tree
, &(reg
->ni
), reg
, COMPARE_FN
);
160 return (reg
->ni
.node
!=NULL
);
163 static bool separated(const WRegionNameInfo
*ni1
,
164 const WRegionNameInfo
*ni2
, int *i1ret
)
169 assert(ni1
->name
!=NULL
);
172 /* Shouldn't happen the way this function is called below; unnamed
173 * regions are before others in the traversal order of the tree.
175 *i1ret
=parseinst_simple(ni1
->name
+ni1
->inst_off
);
179 assert(ni1
->inst_off
>=0 && ni2
->inst_off
>=0);
184 i1
=parseinst_simple(ni1
->name
+ni1
->inst_off
);
190 if(memcmp(ni1
->name
, ni2
->name
, l1
)!=0)
193 i2
=parseinst_simple(ni2
->name
+ni2
->inst_off
);
200 void region_unregister(WRegion
*reg
)
202 if(reg
->ni
.node
!=NULL
){
203 rb_delete_node((Rb_node
)reg
->ni
.node
);
207 if(reg
->ni
.name
!=NULL
){
215 static bool make_full_name(WRegionNameInfo
*ni
, const char *name
, int inst
,
218 assert(ni
->name
==NULL
);
220 if(inst
==0 && !append_always
)
221 ni
->name
=scopy(name
);
223 libtu_asprintf(&(ni
->name
), "%s<%d>", name
, inst
);
228 ni
->inst_off
=strlen(name
);
234 static bool do_use_name(WRegion
*reg
, WNamespace
*ns
, const char *name
,
235 int instrq
, bool failchange
)
238 WRegionNameInfo ni
={NULL
, 0, NULL
};
239 const char *dummy
=NULL
;
244 parsed_inst
=parseinst(name
, &dummy
);
249 /* If there's something that looks like an instance at the end of
250 * name, we will append the instance number.
252 if(parsed_inst
>=0 && inst
==0 && failchange
)
256 WRegionNameInfo tmpni
;
257 tmpni
.name
=(char*)name
;
258 tmpni
.inst_off
=-instrq
-1;
259 node
=rb_find_gkey_n(ns
->rb
, &tmpni
, COMPARE_FN
, &found
);
261 if(rb_val(node
)==(void*)reg
){
262 /* The region already has the requested name */
273 WRegionNameInfo tmpni
;
278 tmpni
.name
=(char*)name
;
280 node
=rb_find_gkey_n(ns
->rb
, &tmpni
, COMPARE_FN
, &found
);
284 Rb_node next
=rb_next(node
);
286 if(rb_val(node
)==(void*)reg
){
287 /* The region already has a name of requested form */
291 if(next
==rb_nil(ns
->rb
) ||
292 separated((const WRegionNameInfo
*)node
->k
.key
,
293 (const WRegionNameInfo
*)next
->k
.key
, &inst
)){
294 /* 'inst' should be next free instance after increment
295 * as separation was greater then one.
301 /* 'inst' should be instance of next after increment
302 * as separation was one.
310 if(!make_full_name(&ni
, name
, inst
, parsed_inst
>=0))
314 rb_find_gkey_n(ns->rb, &ni, COMPARE_FN, &found);
319 region_unregister(reg
);
321 reg
->ni
.name
=ni
.name
;
322 reg
->ni
.inst_off
=ni
.inst_off
;
324 if(!insert_reg(ns
->rb
, reg
)){
335 static bool use_name_anyinst(WRegion
*reg
, WNamespace
*ns
, const char *name
)
337 return do_use_name(reg
, ns
, name
, -1, FALSE
);
341 static bool use_name_exact(WRegion
*reg
, WNamespace
*ns
, const char *name
)
343 return do_use_name(reg
, ns
, name
, 0, TRUE
);
347 static bool use_name_parseany(WRegion
*reg
, WNamespace
*ns
, const char *name
)
350 const char *startinst
;
352 inst
=parseinst(name
, &startinst
);
355 int realnamelen
=startinst
-name
;
356 char *realname
=ALLOC_N(char, realnamelen
+1);
358 memcpy(realname
, name
, realnamelen
);
359 realname
[realnamelen
]='\0';
360 retval
=do_use_name(reg
, ns
, realname
, inst
, FALSE
);
366 return do_use_name(reg
, ns
, name
, 0, FALSE
);
378 * Returns the name for \var{reg}.
382 const char *region_name(WRegion
*reg
)
388 static bool do_set_name(bool (*fn
)(WRegion
*reg
, WNamespace
*ns
, const char *p
),
389 WRegion
*reg
, WNamespace
*ns
, const char *p
)
394 if(!initialise_ns(ns
))
404 if(nm
==NULL
|| *nm
=='\0'){
405 region_unregister(reg
);
406 ret
=insert_reg(ns
->rb
, reg
);
414 region_notify_change(reg
, ioncore_g
.notifies
.name
);
420 bool region_register(WRegion
*reg
)
422 assert(reg
->ni
.name
==NULL
);
424 if(!initialise_ns(&ioncore_internal_ns
))
427 return use_name_anyinst(reg
, &ioncore_internal_ns
, OBJ_TYPESTR(reg
));
431 bool clientwin_register(WClientWin
*cwin
)
433 WRegion
*reg
=(WRegion
*)cwin
;
435 assert(reg
->ni
.name
==NULL
);
437 if(!initialise_ns(&ioncore_clientwin_ns
))
440 return insert_reg(ioncore_clientwin_ns
.rb
, (WRegion
*)cwin
);
445 * Set the name of \var{reg} to \var{p}. If the name is already in use,
446 * an instance number suffix \codestr{<n>} will be attempted. If \var{p} has
447 * such a suffix, it will be modified, otherwise such a suffix will be
448 * added. Setting \var{p} to nil will cause current name to be removed.
451 bool region_set_name(WRegion
*reg
, const char *p
)
453 /* return do_set_name(use_name_parseany, reg, &ioncore_internal_ns, p);*/
454 return do_set_name(use_name_parseany
, reg
,
455 OBJ_IS(reg
, WClientWin
) ? &ioncore_clientwin_ns
: &ioncore_internal_ns
,
461 * Similar to \fnref{WRegion.set_name} except if the name is already in use,
462 * other instance numbers will not be attempted. The string \var{p} should
463 * not contain a \codestr{<n>} suffix or this function will fail.
466 bool region_set_name_exact(WRegion
*reg
, const char *p
)
468 return do_set_name(use_name_exact
, reg
, &ioncore_internal_ns
, p
);
472 bool clientwin_set_name(WClientWin
*cwin
, const char *p
)
474 return do_set_name(use_name_anyinst
, (WRegion
*)cwin
,
475 &ioncore_clientwin_ns
, p
);
482 /*{{{ Lookup and list */
485 static WRegion
*do_lookup_region(WNamespace
*ns
, const char *cname
,
491 const char *instptr
=NULL
;
493 if(cname
==NULL
|| !ns
->initialised
)
496 parseinst(cname
, &instptr
);
497 assert(instptr
!=NULL
);
499 ni
.name
=(char*)cname
;
500 ni
.inst_off
=instptr
-cname
;
502 node
=rb_find_gkey_n(ns
->rb
, &ni
, COMPARE_FN
, &found
);
507 if(typenam
!=NULL
&& !obj_is_str((Obj
*)node
->v
.val
, typenam
))
510 return (WRegion
*)node
->v
.val
;
515 * Attempt to find a non-client window region with name \var{name} and type
516 * inheriting \var{typenam}.
520 WRegion
*ioncore_lookup_region(const char *name
, const char *typenam
)
522 return do_lookup_region(&ioncore_internal_ns
, name
, typenam
);
527 * Attempt to find a client window with name \var{name}.
531 WClientWin
*ioncore_lookup_clientwin(const char *name
)
533 return (WClientWin
*)do_lookup_region(&ioncore_clientwin_ns
, name
,
538 static bool do_list(ExtlFn fn
, WNamespace
*ns
, const char *typenam
)
545 rb_traverse(node
, ns
->rb
){
546 WRegion
*reg
=(WRegion
*)node
->v
.val
;
550 if(typenam
!=NULL
&& !obj_is_str((Obj
*)reg
, typenam
))
553 if(!extl_iter_obj(fn
, (Obj
*)reg
))
562 * Iterate over all non-client window regions with (inherited) class
563 * \var{typenam} until \var{iterfn} returns \code{false}.
564 * The function is called in protected mode.
565 * This routine returns \code{true} if it reaches the end of list
566 * without this happening.
570 bool ioncore_region_i(ExtlFn fn
, const char *typenam
)
572 return do_list(fn
, &ioncore_internal_ns
, typenam
);
577 * Iterate over client windows until \var{iterfn} returns \code{false}.
578 * The function is called in protected mode.
579 * This routine returns \code{true} if it reaches the end of list
580 * without this happening.
584 bool ioncore_clientwin_i(ExtlFn fn
)
586 return do_list(fn
, &ioncore_clientwin_ns
, NULL
);
596 const char *region_displayname(WRegion
*reg
)
598 const char *ret
=NULL
;
599 CALL_DYN_RET(ret
, const char *, region_displayname
, reg
, (reg
));
604 char *region_make_label(WRegion
*reg
, int maxw
, GrBrush
*brush
)
606 const char *name
=region_displayname(reg
);
611 return grbrush_make_label(brush
, name
, maxw
);