1/*
2 * ------------------------------------------------------------------------
3 *      PACKAGE:  [incr Tk]
4 *  DESCRIPTION:  Building mega-widgets with [incr Tcl]
5 *
6 *  [incr Tk] provides a framework for building composite "mega-widgets"
7 *  using [incr Tcl] classes.  It defines a set of base classes that are
8 *  specialized to create all other widgets.
9 *
10 *  This part defines some utility procedures that are useful for
11 *  [incr Tk].
12 *
13 * ========================================================================
14 *  AUTHOR:  Michael J. McLennan
15 *           Bell Labs Innovations for Lucent Technologies
16 *           mmclennan@lucent.com
17 *           http://www.tcltk.com/itcl
18 *
19 *     RCS:  $Id: itk_util.c,v 1.1 1998/07/27 18:45:25 stanton Exp $
20 * ========================================================================
21 *           Copyright (c) 1993-1998  Lucent Technologies, Inc.
22 * ------------------------------------------------------------------------
23 * See the file "license.terms" for information on usage and redistribution
24 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
25 */
26#include "itk.h"
27
28
29/*
30 * ------------------------------------------------------------------------
31 *  Itk_OptListInit()
32 *
33 *  Initializes an ordered option list, allocating a certain amount of
34 *  memory for an initial option list.
35 * ------------------------------------------------------------------------
36 */
37void
38Itk_OptListInit(olist, options)
39    ItkOptList *olist;       /* list to be initialized */
40    Tcl_HashTable *options;  /* table containing the real option entries */
41{
42    olist->options = options;
43    olist->len = 0;
44    olist->max = 10;
45    olist->list = (Tcl_HashEntry**)ckalloc(
46        (unsigned)(olist->max*sizeof(Tcl_HashEntry*))
47    );
48}
49
50
51/*
52 * ------------------------------------------------------------------------
53 *  Itk_OptListFree()
54 *
55 *  Frees an ordered option list created by Itk_OptListInit().
56 *  This only frees the memory associated with the list, not the
57 *  list itself.
58 * ------------------------------------------------------------------------
59 */
60void
61Itk_OptListFree(olist)
62    ItkOptList *olist;     /* list to be freed */
63{
64    ckfree((char*)olist->list);
65    olist->len = olist->max = 0;
66}
67
68
69/*
70 * ------------------------------------------------------------------------
71 *  Itk_OptListAdd()
72 *
73 *  Adds the hash table entry for an option like '-background' to an
74 *  ordered list of options.  The list is kept in alphabetical order,
75 *  so that it can be searched quickly and printed out in order.
76 * ------------------------------------------------------------------------
77 */
78void
79Itk_OptListAdd(olist, entry)
80    ItkOptList *olist;     /* ordered list */
81    Tcl_HashEntry *entry;  /* entry to be added to the list */
82{
83    int i, first, last, cmp, pos, size;
84    Tcl_HashEntry** newOrder;
85    char *swname, *optname;
86
87    /*
88     *  Make sure that the option list is big enough.  Resize
89     *  if needed.
90     */
91    if (olist->len >= olist->max) {
92        size = olist->max*sizeof(Tcl_HashEntry*);
93        newOrder = (Tcl_HashEntry**)ckalloc((unsigned)2*size);
94        memcpy((VOID*)newOrder, (VOID*)olist->list, (size_t)size);
95        ckfree((char*)olist->list);
96
97        olist->list = newOrder;
98        olist->max *= 2;
99    }
100
101    /*
102     *  Perform a binary search to find the option switch quickly.
103     */
104    first = 0;
105    last  = olist->len-1;
106    swname = Tcl_GetHashKey(olist->options, entry) + 1;
107
108    while (last >= first) {
109        pos = (first+last)/2;
110        optname = Tcl_GetHashKey(olist->options, olist->list[pos]) + 1;
111        if (*swname == *optname) {
112            cmp = strcmp(swname, optname);
113            if (cmp == 0) {
114                break;    /* found it! */
115            }
116        }
117        else if (*swname < *optname) {
118            cmp = -1;
119        }
120        else {
121            cmp = 1;
122        }
123
124        if (cmp > 0)
125            first = pos+1;
126        else
127            last = pos-1;
128    }
129
130    /*
131     *  If a matching entry was not found, then insert one.
132     */
133    if (last < first) {
134        pos = first;
135
136        for (i=olist->len; i > pos; i--) {
137            olist->list[i] = olist->list[i-1];
138        }
139        olist->list[pos] = entry;
140        olist->len++;
141    }
142}
143
144
145/*
146 * ------------------------------------------------------------------------
147 *  Itk_OptListRemove()
148 *
149 *  Removes a hash table entry from an ordered list of options.
150 *  This negates the action of Itk_OptionListAdd(), and is usually
151 *  called when an option is completely removed from a mega-widget.
152 *  This should be called before the entry is removed from the
153 *  real option table.
154 * ------------------------------------------------------------------------
155 */
156void
157Itk_OptListRemove(olist, entry)
158    ItkOptList *olist;     /* ordered list */
159    Tcl_HashEntry *entry;  /* entry to be removed from the list */
160{
161    int pos = 0;
162    int i, first, last, cmp;
163    char *swname, *optname;
164
165    first = 0;
166    last  = olist->len-1;
167    swname = Tcl_GetHashKey(olist->options, entry) + 1;
168
169    while (last >= first) {
170        pos = (first+last)/2;
171        optname = Tcl_GetHashKey(olist->options, olist->list[pos]) + 1;
172        if (*swname == *optname) {
173            cmp = strcmp(swname, optname);
174            if (cmp == 0) {
175                break;    /* found it! */
176            }
177        }
178        else if (*swname < *optname) {
179            cmp = -1;
180        }
181        else {
182            cmp = 1;
183        }
184
185        if (cmp > 0)
186            first = pos+1;
187        else
188            last = pos-1;
189    }
190
191    /*
192     *  If a matching entry was found, then remove it.
193     */
194    if (last >= first) {
195        olist->len--;
196        for (i=pos; i < olist->len; i++) {
197            olist->list[i] = olist->list[i+1];
198        }
199    }
200}
201