trunk: changeset 1925
[notion/jeffpc.git] / ioncore / names.c
blobf1bba1059d452b9a465e1d1ef0567c1ee8d6d485
1 /*
2 * ion/ioncore/names.c
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.
13 #include <string.h>
14 #include <limits.h>
16 #include <libtu/rb.h>
17 #include <libtu/minmax.h>
18 #include <libtu/objp.h>
20 #include "common.h"
21 #include "region.h"
22 #include "clientwin.h"
23 #include "names.h"
24 #include "strings.h"
25 #include "gr.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)
38 if(ns->initialised)
39 return TRUE;
41 if(ns->rb==NULL)
42 ns->rb=make_rb();
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;
53 int inst;
54 int l=strlen(name);
56 *startinst=name+l;
58 if(name[l-1]!='>')
59 return -1;
61 p2=strrchr(name, '<');
62 if(p2==NULL)
63 return -1;
65 inst=strtoul(p2+1, (char**)&p3, 10);
67 if(inst<0 || p3!=name+l-1)
68 return -1;
70 *startinst=p2;
71 return inst;
75 static int parseinst_simple(const char *inststr)
77 const char *end=NULL;
78 int inst;
80 if(*inststr=='\0')
81 return 0;
83 if(*inststr=='<'){
84 inst=strtoul(inststr+1, (char**)&end, 10);
86 if(inst>=0 && end!=NULL && *end=='>')
87 return inst;
90 warn(TR("Corrupt instance number %s."), inststr);
91 return -1;
95 #define COMPARE_FN ((Rb_compfn*)compare_nameinfos)
97 static int compare_nameinfos(const WRegionNameInfo *ni1,
98 const WRegionNameInfo *ni2)
100 int l1, l2;
101 int i1, i2;
102 int mc;
104 /* Handle unnamed regions. */
106 if(ni1->name==NULL){
107 if(ni2->name!=NULL)
108 return 1;
109 return (ni1==ni2 ? 0 : (ni1<ni2 ? -1 : 1));
110 }else if(ni2->name==NULL){
111 return -1;
114 /* Special case: inst_off<0 means that -inst_off-1 is the actual
115 * instance number and the name does not contain it.
118 if(ni1->inst_off>=0)
119 l1=ni1->inst_off;
120 else
121 l1=strlen(ni1->name);
123 if(ni2->inst_off>=0)
124 l2=ni2->inst_off;
125 else
126 l2=strlen(ni2->name);
128 /* Check name part first */
130 mc=strncmp(ni1->name, ni2->name, minof(l1, l2));
132 if(mc!=0)
133 return mc;
135 if(l1!=l2)
136 return (l1<l2 ? -1 : 1);
138 /* Same name, different instance */
140 if(ni1->inst_off>=0)
141 i1=parseinst_simple(ni1->name+ni1->inst_off);
142 else
143 i1=-ni1->inst_off-1;
145 if(ni2->inst_off>=0)
146 i2=parseinst_simple(ni2->name+ni2->inst_off);
147 else
148 i2=-ni2->inst_off-1;
150 if(i1!=i2)
151 return (i1<i2 ? -1 : 1);
153 /* Same name and instance */
155 return 0;
159 static bool separated(const WRegionNameInfo *ni1,
160 const WRegionNameInfo *ni2, int *i1ret)
162 int l1, l2;
163 int i1, i2;
164 int mc;
166 assert(ni1->name!=NULL);
168 if(ni2->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);
173 return TRUE;
176 assert(ni1->inst_off>=0 && ni2->inst_off>=0);
178 l1=ni1->inst_off;
179 l2=ni2->inst_off;
181 i1=parseinst_simple(ni1->name+ni1->inst_off);
182 *i1ret=i1;
184 if(l1!=l2)
185 return TRUE;
187 if(memcmp(ni1->name, ni2->name, l1)!=0)
188 return TRUE;
190 i2=parseinst_simple(ni2->name+ni2->inst_off);
192 return (i2>(i1+1));
197 void region_do_unuse_name(WRegion *reg, bool insert_unnamed)
199 WNamespace *ns=reg->ni.namespaceinfo;
200 Rb_node node;
201 int found=0;
203 if(ns==NULL || !ns->initialised)
204 return;
206 node=rb_find_gkey_n(ns->rb, &(reg->ni), COMPARE_FN, &found);
208 if(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){
216 free(reg->ni.name);
217 reg->ni.name=NULL;
218 reg->ni.inst_off=0;
221 if(insert_unnamed){
222 if(rb_insertg(ns->rb, &(reg->ni), reg, COMPARE_FN))
223 return;
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,
238 bool append_always)
240 assert(ni->name==NULL);
242 if(inst==0 && !append_always)
243 ni->name=scopy(name);
244 else
245 libtu_asprintf(&(ni->name), "%s<%d>", name, inst);
247 if(ni->name==NULL)
248 return FALSE;
250 ni->inst_off=strlen(name);
252 return TRUE;
256 static bool do_use_name(WRegion *reg, WNamespace *ns, const char *name,
257 int instrq, bool failchange)
259 int parsed_inst=-1;
260 WRegionNameInfo ni={NULL, 0, NULL};
261 const char *dummy=NULL;
262 Rb_node node;
263 int inst=-1;
264 int found=0;
266 parsed_inst=parseinst(name, &dummy);
268 if(!ns->initialised)
269 return FALSE;
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)
275 return FALSE;
277 if(instrq>=0){
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);
282 if(found){
283 if(rb_val(node)==(void*)reg){
284 /* The region already has the requested name */
285 return TRUE;
287 if(failchange)
288 return FALSE;
289 }else{
290 inst=instrq;
294 if(inst<0){
295 WRegionNameInfo tmpni;
297 found=0;
298 inst=0;
300 tmpni.name=(char*)name;
301 tmpni.inst_off=-1;
302 node=rb_find_gkey_n(ns->rb, &tmpni, COMPARE_FN, &found);
304 if(found){
305 while(1){
306 Rb_node next=rb_next(node);
308 if(rb_val(node)==(void*)reg){
309 /* The region already has a name of requested form */
310 return TRUE;
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.
319 inst++;
320 break;
323 /* 'inst' should be instance of next after increment
324 * as separation was one.
326 inst++;
327 node=next;
332 if(!make_full_name(&ni, name, inst, parsed_inst>=0))
333 return FALSE;
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)){
342 free(reg->ni.name);
343 reg->ni.name=NULL;
344 reg->ni.inst_off=0;
345 rb_insertg(ns->rb, &(reg->ni), reg, COMPARE_FN);
346 return FALSE;
349 return TRUE;
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)
367 int l, inst;
368 const char *startinst;
370 l=strlen(name);
372 inst=parseinst(name, &startinst);
373 if(inst>=0){
374 int realnamelen=startinst-name;
375 char *realname=ALLOC_N(char, realnamelen+1);
376 if(realname!=NULL){
377 bool retval;
378 memcpy(realname, name, realnamelen);
379 realname[realnamelen]='\0';
380 retval=do_use_name(reg, ns, realname, inst, FALSE);
381 free(realname);
382 return retval;
386 return do_use_name(reg, ns, name, 0, FALSE);
391 /*}}}*/
394 /*{{{ Interface */
397 /*EXTL_DOC
398 * Returns the name for \var{reg}.
400 EXTL_EXPORT_MEMBER
401 const char *region_name(WRegion *reg)
403 return reg->ni.name;
407 char *region_make_label(WRegion *reg, int maxw, GrBrush *brush)
409 if(reg->ni.name==NULL)
410 return 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)
419 bool ret=TRUE;
420 char *nm=NULL;
422 if(p!=NULL){
423 nm=scopy(p);
424 if(nm==NULL)
425 return FALSE;
426 str_stripws(nm);
429 if(nm==NULL || *nm=='\0'){
430 region_do_unuse_name(reg, TRUE);
431 }else{
432 if(!initialise_ns(ns))
433 return FALSE;
434 ret=fn(reg, ns, nm);
437 if(nm!=NULL)
438 free(nm);
440 region_notify_change(reg);
442 return ret;
446 bool region_init_name(WRegion *reg)
448 if(OBJ_IS(reg, WClientWin)){
449 if(!initialise_ns(&ioncore_clientwin_ns))
450 return FALSE;
451 assert(reg->ni.name==NULL);
452 reg->ni.namespaceinfo=&ioncore_clientwin_ns;
453 return (rb_insertg(ioncore_clientwin_ns.rb, &(reg->ni), reg,
454 COMPARE_FN)
455 !=NULL);
458 if(!initialise_ns(&ioncore_internal_ns))
459 return FALSE;
461 return use_name_anyinst(reg, &ioncore_internal_ns, OBJ_TYPESTR(reg));
465 /*EXTL_DOC
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.
471 EXTL_EXPORT_MEMBER
472 bool region_set_name(WRegion *reg, const char *p)
474 return do_set_name(use_name_parseany, reg, &ioncore_internal_ns, p);
478 /*EXTL_DOC
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.
483 EXTL_EXPORT_MEMBER
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);
497 /*}}}*/
500 /*{{{ Lookup and list */
503 static WRegion *do_lookup_region(WNamespace *ns, const char *cname,
504 const char *typenam)
506 WRegionNameInfo ni;
507 Rb_node node;
508 int found=0;
509 const char *instptr=NULL;
511 if(cname==NULL || !ns->initialised)
512 return NULL;
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);
522 if(!found)
523 return NULL;
525 return (WRegion*)node->v.val;
529 /*EXTL_DOC
530 * Attempt to find a non-client window region with name \var{name} and type
531 * inheriting \var{typenam}.
533 EXTL_EXPORT
534 WRegion *ioncore_lookup_region(const char *name, const char *typenam)
536 return do_lookup_region(&ioncore_internal_ns, name, typenam);
540 /*EXTL_DOC
541 * Attempt to find a client window with name \var{name}.
543 EXTL_EXPORT
544 WClientWin *ioncore_lookup_clientwin(const char *name)
546 return (WClientWin*)do_lookup_region(&ioncore_clientwin_ns, name,
547 "WClientWin");
551 static ExtlTab do_list(WNamespace *ns, const char *typenam)
553 ExtlTab tab;
554 Rb_node node;
555 int n=0;
557 if(!ns->initialised)
558 return extl_table_none();
560 tab=extl_create_table();
562 rb_traverse(node, ns->rb){
563 WRegion *reg=(WRegion*)node->v.val;
565 assert(reg!=NULL);
567 if(typenam!=NULL && !obj_is_str((Obj*)reg, typenam))
568 continue;
570 if(extl_table_seti_o(tab, n+1, (Obj*)reg))
571 n++;
574 return tab;
578 /*EXTL_DOC
579 * Find all non-client window regions inheriting \var{typenam}.
581 EXTL_EXPORT
582 ExtlTab ioncore_region_list(const char *typenam)
584 return do_list(&ioncore_internal_ns, typenam);
588 /*EXTL_DOC
589 * Return a list of all client windows.
591 EXTL_EXPORT
592 ExtlTab ioncore_clientwin_list()
594 return do_list(&ioncore_clientwin_ns, NULL);
598 /*}}}*/