4 * Copyright (c) Tuomo Valkonen 1999-2007.
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>
19 #include <libextl/extl.h>
24 #include "clientwin.h"
30 /*{{{ Implementation */
33 WNamespace ioncore_internal_ns={NULL, FALSE};
34 WNamespace ioncore_clientwin_ns={NULL, FALSE};
37 static bool initialise_ns(WNamespace *ns)
45 ns->initialised=(ns->rb!=NULL);
47 return ns->initialised;
51 static int parseinst(const char *name, const char **startinst)
53 const char *p2, *p3=NULL;
62 p2=strrchr(name, '<');
66 inst=strtoul(p2+1, (char**)&p3, 10);
68 if(inst<0 || p3!=name+l-1)
76 static int parseinst_simple(const char *inststr)
85 inst=strtoul(inststr+1, (char**)&end, 10);
87 if(inst>=0 && end!=NULL && *end=='>')
91 warn(TR("Corrupt instance number %s."), inststr);
96 #define COMPARE_FN ((Rb_compfn*)compare_nameinfos)
98 static int compare_nameinfos(const WRegionNameInfo *ni1,
99 const WRegionNameInfo *ni2)
105 /* Handle unnamed regions. */
110 return (ni1==ni2 ? 0 : (ni1<ni2 ? -1 : 1));
111 }else if(ni2->name==NULL){
115 /* Special case: inst_off<0 means that -inst_off-1 is the actual
116 * instance number and the name does not contain it.
122 l1=strlen(ni1->name);
127 l2=strlen(ni2->name);
129 /* Check name part first */
131 mc=strncmp(ni1->name, ni2->name, minof(l1, l2));
137 return (l1<l2 ? -1 : 1);
139 /* Same name, different instance */
142 i1=parseinst_simple(ni1->name+ni1->inst_off);
144 i1=-ni1->inst_off-1; /*???*/
147 i2=parseinst_simple(ni2->name+ni2->inst_off);
149 i2=-ni2->inst_off-1; /*???*/
152 return (i1<i2 ? -1 : 1);
154 /* Same name and instance */
159 static bool insert_reg(Rb_node tree, WRegion *reg)
161 assert(reg->ni.node==NULL);
162 reg->ni.node=(void*)rb_insertg(tree, &(reg->ni), reg, COMPARE_FN);
163 return (reg->ni.node!=NULL);
166 static bool separated(const WRegionNameInfo *ni1,
167 const WRegionNameInfo *ni2, int *i1ret)
173 assert(ni1->name!=NULL);
176 /* Shouldn't happen the way this function is called below; unnamed
177 * regions are before others in the traversal order of the tree.
179 *i1ret=parseinst_simple(ni1->name+ni1->inst_off);
183 assert(ni1->inst_off>=0 && ni2->inst_off>=0);
188 i1=parseinst_simple(ni1->name+ni1->inst_off);
194 if(memcmp(ni1->name, ni2->name, l1)!=0)
197 i2=parseinst_simple(ni2->name+ni2->inst_off);
204 void region_unregister(WRegion *reg)
206 if(reg->ni.node!=NULL){
207 rb_delete_node((Rb_node)reg->ni.node);
211 if(reg->ni.name!=NULL){
219 static bool make_full_name(WRegionNameInfo *ni, const char *name, int inst,
222 assert(ni->name==NULL);
224 if(inst==0 && !append_always)
225 ni->name=scopy(name);
227 libtu_asprintf(&(ni->name), "%s<%d>", name, inst);
232 ni->inst_off=strlen(name);
238 static bool do_use_name(WRegion *reg, WNamespace *ns, const char *name,
239 int instrq, bool failchange)
242 WRegionNameInfo ni={NULL, 0, NULL};
243 const char *dummy=NULL;
248 parsed_inst=parseinst(name, &dummy);
253 /* If there's something that looks like an instance at the end of
254 * name, we will append the instance number.
256 if(parsed_inst>=0 && inst==0 && failchange)
260 WRegionNameInfo tmpni;
261 tmpni.name=(char*)name;
262 tmpni.inst_off=-instrq-1;
263 node=rb_find_gkey_n(ns->rb, &tmpni, COMPARE_FN, &found);
265 if(rb_val(node)==(void*)reg){
266 /* The region already has the requested name */
277 WRegionNameInfo tmpni;
282 tmpni.name=(char*)name;
284 node=rb_find_gkey_n(ns->rb, &tmpni, COMPARE_FN, &found);
288 Rb_node next=rb_next(node);
290 if(rb_val(node)==(void*)reg){
291 /* The region already has a name of requested form */
295 if(next==rb_nil(ns->rb) ||
296 separated((const WRegionNameInfo*)node->k.key,
297 (const WRegionNameInfo*)next->k.key, &inst)){
298 /* 'inst' should be next free instance after increment
299 * as separation was greater then one.
305 /* 'inst' should be instance of next after increment
306 * as separation was one.
314 if(!make_full_name(&ni, name, inst, parsed_inst>=0))
318 rb_find_gkey_n(ns->rb, &ni, COMPARE_FN, &found);
323 region_unregister(reg);
325 reg->ni.name=ni.name;
326 reg->ni.inst_off=ni.inst_off;
328 if(!insert_reg(ns->rb, reg)){
339 static bool use_name_anyinst(WRegion *reg, WNamespace *ns, const char *name)
341 return do_use_name(reg, ns, name, -1, FALSE);
345 static bool use_name_exact(WRegion *reg, WNamespace *ns, const char *name)
347 return do_use_name(reg, ns, name, 0, TRUE);
351 static bool use_name_parseany(WRegion *reg, WNamespace *ns, const char *name)
354 const char *startinst;
358 inst=parseinst(name, &startinst);
361 int realnamelen=startinst-name;
362 char *realname=ALLOC_N(char, realnamelen+1);
364 memcpy(realname, name, realnamelen);
365 realname[realnamelen]='\0';
366 retval=do_use_name(reg, ns, realname, inst, FALSE);
372 return do_use_name(reg, ns, name, 0, FALSE);
384 * Returns the name for \var{reg}.
388 const char *region_name(WRegion *reg)
394 static bool do_set_name(bool (*fn)(WRegion *reg, WNamespace *ns, const char *p),
395 WRegion *reg, WNamespace *ns, const char *p)
400 if(!initialise_ns(ns))
410 if(nm==NULL || *nm=='\0'){
411 region_unregister(reg);
412 ret=insert_reg(ns->rb, reg);
420 region_notify_change(reg, ioncore_g.notifies.name);
426 bool region_register(WRegion *reg)
428 assert(reg->ni.name==NULL);
430 if(!initialise_ns(&ioncore_internal_ns))
433 return use_name_anyinst(reg, &ioncore_internal_ns, OBJ_TYPESTR(reg));
437 bool clientwin_register(WClientWin *cwin)
439 WRegion *reg=(WRegion*)cwin;
441 assert(reg->ni.name==NULL);
443 if(!initialise_ns(&ioncore_clientwin_ns))
446 return insert_reg(ioncore_clientwin_ns.rb, (WRegion*)cwin);
451 * Set the name of \var{reg} to \var{p}. If the name is already in use,
452 * an instance number suffix \code{<n>} will be attempted. If \var{p} has
453 * such a suffix, it will be modified, otherwise such a suffix will be
454 * added. Setting \var{p} to nil will cause current name to be removed.
457 bool region_set_name(WRegion *reg, const char *p)
459 /* return do_set_name(use_name_parseany, reg, &ioncore_internal_ns, p);*/
460 return do_set_name(use_name_parseany, reg,
461 OBJ_IS(reg, WClientWin) ? &ioncore_clientwin_ns : &ioncore_internal_ns,
467 * Similar to \fnref{WRegion.set_name} except if the name is already in use,
468 * other instance numbers will not be attempted. The string \var{p} should
469 * not contain a \code{<n>} suffix or this function will fail.
472 bool region_set_name_exact(WRegion *reg, const char *p)
474 return do_set_name(use_name_exact, reg, &ioncore_internal_ns, p);
478 bool clientwin_set_name(WClientWin *cwin, const char *p)
480 return do_set_name(use_name_anyinst, (WRegion*)cwin,
481 &ioncore_clientwin_ns, p);
488 /*{{{ Lookup and list */
491 static WRegion *do_lookup_region(WNamespace *ns, const char *cname,
497 const char *instptr=NULL;
499 if(cname==NULL || !ns->initialised)
502 parseinst(cname, &instptr);
503 assert(instptr!=NULL);
505 ni.name=(char*)cname;
506 ni.inst_off=instptr-cname;
508 node=rb_find_gkey_n(ns->rb, &ni, COMPARE_FN, &found);
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 itself returns \code{true} if it reaches the end of list
569 * without this happening.
573 bool ioncore_region_i(ExtlFn fn, const char *typenam)
575 return do_list(fn, &ioncore_internal_ns, typenam);
580 * Iterate over client windows until \var{iterfn} returns \code{false}.
581 * The function itself returns \code{true} if it reaches the end of list
582 * without this happening.
586 bool ioncore_clientwin_i(ExtlFn fn)
588 return do_list(fn, &ioncore_clientwin_ns, NULL);
598 const char *region_displayname(WRegion *reg)
600 const char *ret=NULL;
601 CALL_DYN_RET(ret, const char *, region_displayname, reg, (reg));
606 char *region_make_label(WRegion *reg, int maxw, GrBrush *brush)
608 const char *name=region_displayname(reg);
613 return grbrush_make_label(brush, name, maxw);