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
)
170 assert(ni1
->name
!=NULL
);
173 /* Shouldn't happen the way this function is called below; unnamed
174 * regions are before others in the traversal order of the tree.
176 *i1ret
=parseinst_simple(ni1
->name
+ni1
->inst_off
);
180 assert(ni1
->inst_off
>=0 && ni2
->inst_off
>=0);
185 i1
=parseinst_simple(ni1
->name
+ni1
->inst_off
);
191 if(memcmp(ni1
->name
, ni2
->name
, l1
)!=0)
194 i2
=parseinst_simple(ni2
->name
+ni2
->inst_off
);
201 void region_unregister(WRegion
*reg
)
203 if(reg
->ni
.node
!=NULL
){
204 rb_delete_node((Rb_node
)reg
->ni
.node
);
208 if(reg
->ni
.name
!=NULL
){
216 static bool make_full_name(WRegionNameInfo
*ni
, const char *name
, int inst
,
219 assert(ni
->name
==NULL
);
221 if(inst
==0 && !append_always
)
222 ni
->name
=scopy(name
);
224 libtu_asprintf(&(ni
->name
), "%s<%d>", name
, inst
);
229 ni
->inst_off
=strlen(name
);
235 static bool do_use_name(WRegion
*reg
, WNamespace
*ns
, const char *name
,
236 int instrq
, bool failchange
)
239 WRegionNameInfo ni
={NULL
, 0, NULL
};
240 const char *dummy
=NULL
;
245 parsed_inst
=parseinst(name
, &dummy
);
250 /* If there's something that looks like an instance at the end of
251 * name, we will append the instance number.
253 if(parsed_inst
>=0 && inst
==0 && failchange
)
257 WRegionNameInfo tmpni
;
258 tmpni
.name
=(char*)name
;
259 tmpni
.inst_off
=-instrq
-1;
260 node
=rb_find_gkey_n(ns
->rb
, &tmpni
, COMPARE_FN
, &found
);
262 if(rb_val(node
)==(void*)reg
){
263 /* The region already has the requested name */
274 WRegionNameInfo tmpni
;
279 tmpni
.name
=(char*)name
;
281 node
=rb_find_gkey_n(ns
->rb
, &tmpni
, COMPARE_FN
, &found
);
285 Rb_node next
=rb_next(node
);
287 if(rb_val(node
)==(void*)reg
){
288 /* The region already has a name of requested form */
292 if(next
==rb_nil(ns
->rb
) ||
293 separated((const WRegionNameInfo
*)node
->k
.key
,
294 (const WRegionNameInfo
*)next
->k
.key
, &inst
)){
295 /* 'inst' should be next free instance after increment
296 * as separation was greater then one.
302 /* 'inst' should be instance of next after increment
303 * as separation was one.
311 if(!make_full_name(&ni
, name
, inst
, parsed_inst
>=0))
315 rb_find_gkey_n(ns->rb, &ni, COMPARE_FN, &found);
320 region_unregister(reg
);
322 reg
->ni
.name
=ni
.name
;
323 reg
->ni
.inst_off
=ni
.inst_off
;
325 if(!insert_reg(ns
->rb
, reg
)){
336 static bool use_name_anyinst(WRegion
*reg
, WNamespace
*ns
, const char *name
)
338 return do_use_name(reg
, ns
, name
, -1, FALSE
);
342 static bool use_name_exact(WRegion
*reg
, WNamespace
*ns
, const char *name
)
344 return do_use_name(reg
, ns
, name
, 0, TRUE
);
348 static bool use_name_parseany(WRegion
*reg
, WNamespace
*ns
, const char *name
)
351 const char *startinst
;
355 inst
=parseinst(name
, &startinst
);
358 int realnamelen
=startinst
-name
;
359 char *realname
=ALLOC_N(char, realnamelen
+1);
361 memcpy(realname
, name
, realnamelen
);
362 realname
[realnamelen
]='\0';
363 retval
=do_use_name(reg
, ns
, realname
, inst
, FALSE
);
369 return do_use_name(reg
, ns
, name
, 0, FALSE
);
381 * Returns the name for \var{reg}.
385 const char *region_name(WRegion
*reg
)
391 static bool do_set_name(bool (*fn
)(WRegion
*reg
, WNamespace
*ns
, const char *p
),
392 WRegion
*reg
, WNamespace
*ns
, const char *p
)
397 if(!initialise_ns(ns
))
407 if(nm
==NULL
|| *nm
=='\0'){
408 region_unregister(reg
);
409 ret
=insert_reg(ns
->rb
, reg
);
417 region_notify_change(reg
, ioncore_g
.notifies
.name
);
423 bool region_register(WRegion
*reg
)
425 assert(reg
->ni
.name
==NULL
);
427 if(!initialise_ns(&ioncore_internal_ns
))
430 return use_name_anyinst(reg
, &ioncore_internal_ns
, OBJ_TYPESTR(reg
));
434 bool clientwin_register(WClientWin
*cwin
)
436 WRegion
*reg
=(WRegion
*)cwin
;
438 assert(reg
->ni
.name
==NULL
);
440 if(!initialise_ns(&ioncore_clientwin_ns
))
443 return insert_reg(ioncore_clientwin_ns
.rb
, (WRegion
*)cwin
);
448 * Set the name of \var{reg} to \var{p}. If the name is already in use,
449 * an instance number suffix \codestr{<n>} will be attempted. If \var{p} has
450 * such a suffix, it will be modified, otherwise such a suffix will be
451 * added. Setting \var{p} to nil will cause current name to be removed.
454 bool region_set_name(WRegion
*reg
, const char *p
)
456 /* return do_set_name(use_name_parseany, reg, &ioncore_internal_ns, p);*/
457 return do_set_name(use_name_parseany
, reg
,
458 OBJ_IS(reg
, WClientWin
) ? &ioncore_clientwin_ns
: &ioncore_internal_ns
,
464 * Similar to \fnref{WRegion.set_name} except if the name is already in use,
465 * other instance numbers will not be attempted. The string \var{p} should
466 * not contain a \codestr{<n>} suffix or this function will fail.
469 bool region_set_name_exact(WRegion
*reg
, const char *p
)
471 return do_set_name(use_name_exact
, reg
, &ioncore_internal_ns
, p
);
475 bool clientwin_set_name(WClientWin
*cwin
, const char *p
)
477 return do_set_name(use_name_anyinst
, (WRegion
*)cwin
,
478 &ioncore_clientwin_ns
, p
);
485 /*{{{ Lookup and list */
488 static WRegion
*do_lookup_region(WNamespace
*ns
, const char *cname
,
494 const char *instptr
=NULL
;
496 if(cname
==NULL
|| !ns
->initialised
)
499 parseinst(cname
, &instptr
);
500 assert(instptr
!=NULL
);
502 ni
.name
=(char*)cname
;
503 ni
.inst_off
=instptr
-cname
;
505 node
=rb_find_gkey_n(ns
->rb
, &ni
, COMPARE_FN
, &found
);
510 if(typenam
!=NULL
&& !obj_is_str((Obj
*)node
->v
.val
, typenam
))
513 return (WRegion
*)node
->v
.val
;
518 * Attempt to find a non-client window region with name \var{name} and type
519 * inheriting \var{typenam}.
523 WRegion
*ioncore_lookup_region(const char *name
, const char *typenam
)
525 return do_lookup_region(&ioncore_internal_ns
, name
, typenam
);
530 * Attempt to find a client window with name \var{name}.
534 WClientWin
*ioncore_lookup_clientwin(const char *name
)
536 return (WClientWin
*)do_lookup_region(&ioncore_clientwin_ns
, name
,
541 static bool do_list(ExtlFn fn
, WNamespace
*ns
, const char *typenam
)
549 rb_traverse(node
, ns
->rb
){
550 WRegion
*reg
=(WRegion
*)node
->v
.val
;
554 if(typenam
!=NULL
&& !obj_is_str((Obj
*)reg
, typenam
))
557 if(!extl_iter_obj(fn
, (Obj
*)reg
))
566 * Iterate over all non-client window regions with (inherited) class
567 * \var{typenam} until \var{iterfn} returns \code{false}.
568 * The function is called in protected mode.
569 * This routine returns \code{true} if it reaches the end of list
570 * without this happening.
574 bool ioncore_region_i(ExtlFn fn
, const char *typenam
)
576 return do_list(fn
, &ioncore_internal_ns
, typenam
);
581 * Iterate over client windows until \var{iterfn} returns \code{false}.
582 * The function is called in protected mode.
583 * This routine returns \code{true} if it reaches the end of list
584 * without this happening.
588 bool ioncore_clientwin_i(ExtlFn fn
)
590 return do_list(fn
, &ioncore_clientwin_ns
, NULL
);
600 const char *region_displayname(WRegion
*reg
)
602 const char *ret
=NULL
;
603 CALL_DYN_RET(ret
, const char *, region_displayname
, reg
, (reg
));
608 char *region_make_label(WRegion
*reg
, int maxw
, GrBrush
*brush
)
610 const char *name
=region_displayname(reg
);
615 return grbrush_make_label(brush
, name
, maxw
);