The R Project SVN R

Rev

Rev 28991 | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 28991 Rev 29216
1
/*
1
/*
2
 * GraphApp - Cross-Platform Graphics Programming Library.
2
 * GraphApp - Cross-Platform Graphics Programming Library.
3
 *
3
 *
4
 * File: objects.c -- maintain internal info about graphical objects.
4
 * File: objects.c -- maintain internal info about graphical objects.
5
 * Platform: Windows  Version: 2.35  Date: 1998/04/04
5
 * Platform: Windows  Version: 2.35  Date: 1998/04/04
6
 *
6
 *
7
 * Version: 1.00  Changes: Original version by Lachlan Patrick.
7
 * Version: 1.00  Changes: Original version by Lachlan Patrick.
8
 * Version: 2.00  Changes: Total overhaul - new object class system.
8
 * Version: 2.00  Changes: Total overhaul - new object class system.
9
 * Version: 2.15  Changes: Objects are Transparent by default.
9
 * Version: 2.15  Changes: Objects are Transparent by default.
10
 * Version: 2.20  Changes: Faster and better exiting.
10
 * Version: 2.20  Changes: Faster and better exiting.
11
 * Version: 2.30  Changes: Now uses array(), not malloc().
11
 * Version: 2.30  Changes: Now uses array(), not malloc().
12
 * Version: 2.35  Changes: New delayed deletion technique.
12
 * Version: 2.35  Changes: New delayed deletion technique.
13
 */
13
 */
14
 
14
 
15
/* Copyright (C) 1993-1998 Lachlan Patrick
15
/* Copyright (C) 1993-1998 Lachlan Patrick
16
 
16
 
17
   This file is part of GraphApp, a cross-platform C graphics library.
17
   This file is part of GraphApp, a cross-platform C graphics library.
18
 
18
 
19
   GraphApp is free software; you can redistribute it and/or modify it
19
   GraphApp is free software; you can redistribute it and/or modify it
20
   under the terms of the GNU Library General Public License.
20
   under the terms of the GNU Library General Public License.
21
   GraphApp is distributed in the hope that it will be useful, but
21
   GraphApp is distributed in the hope that it will be useful, but
22
   WITHOUT ANY WARRANTY.
22
   WITHOUT ANY WARRANTY.
23
 
23
 
24
   See the file COPYLIB.TXT for details.
24
   See the file COPYLIB.TXT for details.
25
 
25
 
26
   Copyright (C) 2004 The R Foundation
26
   Copyright (C) 2004 The R Foundation
27
*/
27
*/
28
 
28
 
29
#include "internal.h"
29
#include "internal.h"
30
 
30
 
31
/*
31
/*
32
 *  The current implementation of the list of objects
32
 *  The current implementation of the list of objects
33
 *  uses a linked list of primary objects, each object
33
 *  uses a linked list of primary objects, each object
34
 *  potentially having a list of children associated with it
34
 *  potentially having a list of children associated with it
35
 *
35
 *
36
 *  e.g. window
36
 *  e.g. window
37
 *        +-> menubar -> button -> scrollbar -> textfield
37
 *        +-> menubar -> button -> scrollbar -> textfield
38
 *             +-> menu
38
 *             +-> menu
39
 *                  +-> menuitem -> menuitem -> menuitem ...
39
 *                  +-> menuitem -> menuitem -> menuitem ...
40
 *
40
 *
41
 *  The base_object is at the top of the hierarchy.
41
 *  The base_object is at the top of the hierarchy.
42
 */
42
 */
43
 
43
 
44
	static object base_object = NULL;
44
	static object base_object = NULL;
45
 
45
 
46
/*
46
/*
47
 *  Initialise the base_object and set the list to be empty.
47
 *  Initialise the base_object and set the list to be empty.
48
 */
48
 */
49
PROTECTED
49
PROTECTED
50
void init_objects(void)
50
void init_objects(void)
51
{
51
{
52
	if (base_object)
52
	if (base_object)
53
		return;
53
		return;
54
 
54
 
55
	base_object = create(objinfo);
55
	base_object = create(objinfo);
56
 
56
 
57
	if (! base_object)
57
	if (! base_object)
58
		apperror("Out of memory (init_objects)!");
58
		apperror("Out of memory (init_objects)!");
59
 
59
 
60
	base_object->kind = BaseObject;
60
	base_object->kind = BaseObject;
61
	base_object->next = base_object->prev = base_object;
61
	base_object->next = base_object->prev = base_object;
62
	base_object->parent = base_object->child = NULL;
62
	base_object->parent = base_object->child = NULL;
63
}
63
}
64
 
64
 
65
static void add_object(object obj, object parent)
65
static void add_object(object obj, object parent)
66
{
66
{
67
	/* All objects have a parent. */
67
	/* All objects have a parent. */
68
	if (! parent)
68
	if (! parent)
69
		parent = base_object;
69
		parent = base_object;
70
 
70
 
71
	if (parent->child)
71
	if (parent->child)
72
	{	/* add to end of child circularly linked list */
72
	{	/* add to end of child circularly linked list */
73
		obj->prev = parent->child->prev;
73
		obj->prev = parent->child->prev;
74
		obj->next = parent->child;
74
		obj->next = parent->child;
75
		obj->prev->next = obj;
75
		obj->prev->next = obj;
76
		obj->next->prev = obj;
76
		obj->next->prev = obj;
77
	} else {
77
	} else {
78
		/* create new child list */
78
		/* create new child list */
79
		obj->next = obj->prev = obj;
79
		obj->next = obj->prev = obj;
80
		parent->child = obj;
80
		parent->child = obj;
81
	}
81
	}
82
	obj->parent = parent;
82
	obj->parent = parent;
83
 
83
 
84
#if DEBUG
84
#if DEBUG
85
	print_object_list();
85
	print_object_list();
86
#endif
86
#endif
87
}
87
}
88
 
88
 
89
static void remove_object(object obj)
89
static void remove_object(object obj)
90
{
90
{
91
	if ((obj->next) && (obj->next != obj)) {
91
	if ((obj->next) && (obj->next != obj)) {
92
		obj->prev->next = obj->next;
92
		obj->prev->next = obj->next;
93
		obj->next->prev = obj->prev;
93
		obj->next->prev = obj->prev;
94
	} else {
94
	} else {
95
		obj->next = obj->prev = NULL;
95
		obj->next = obj->prev = NULL;
96
	}
96
	}
97
 
97
 
98
	if (obj->parent)
98
	if (obj->parent)
99
		if (obj->parent->child == obj)
99
		if (obj->parent->child == obj)
100
			obj->parent->child = obj->next;
100
			obj->parent->child = obj->next;
101
}
101
}
102
 
102
 
103
/*
103
/*
104
 *  Bring an object to the front of its sibling list.
104
 *  Bring an object to the front of its sibling list.
105
 *  Useful for keeping track of which window is up front.
105
 *  Useful for keeping track of which window is up front.
106
 */
106
 */
107
PROTECTED
107
PROTECTED
108
void move_to_front(object obj)
108
void move_to_front(object obj)
109
{
109
{
110
	object parent = obj->parent;
110
	object parent = obj->parent;
111
	remove_object(obj);	 /* Remove it so as to reorder list. */
111
	remove_object(obj);	 /* Remove it so as to reorder list. */
112
	add_object(obj, parent); /* Add to end of list. */
112
	add_object(obj, parent); /* Add to end of list. */
113
	parent->child = obj;	 /* Move to front. */
113
	parent->child = obj;	 /* Move to front. */
114
}
114
}
115
 
115
 
116
/*
116
/*
117
 *  Call a function on all objects in a sibling list.
117
 *  Call a function on all objects in a sibling list.
118
 *  Only applies up to the end of the list, i.e. up to and
118
 *  Only applies up to the end of the list, i.e. up to and
119
 *  including the last element in the circular list.
119
 *  including the last element in the circular list.
120
 *  Used to disable or enable windows when modal windows are used.
120
 *  Used to disable or enable windows when modal windows are used.
121
 */
121
 */
122
void apply_to_list(object first, actionfn fn)
122
void apply_to_list(object first, actionfn fn)
123
{
123
{
124
	object start = first->parent->child;
124
	object start = first->parent->child;
125
	object obj = first;
125
	object obj = first;
126
 
126
 
127
	do {
127
	do {
128
		fn(obj);
128
		fn(obj);
129
		obj = obj->next;
129
		obj = obj->next;
130
	} while (obj != start);
130
	} while (obj != start);
131
}
131
}
132
 
132
 
133
/*
133
/*
134
 *  When objects are deleted using del() they are added to
134
 *  When objects are deleted using del() they are added to
135
 *  the end of a circular list, which is traversed at the end
135
 *  the end of a circular list, which is traversed at the end
136
 *  of each call to doevent(). This traversal actually deletes
136
 *  of each call to doevent(). This traversal actually deletes
137
 *  the objects in that list. The del_base object is at
137
 *  the objects in that list. The del_base object is at
138
 *  the head of this list and is the first object to be
138
 *  the head of this list and is the first object to be
139
 *  deleted.
139
 *  deleted.
140
 */
140
 */
141
 
141
 
142
	typedef struct delnode delnode;
142
	typedef struct delnode delnode;
143
	struct delnode {
143
	struct delnode {
144
		object    obj;	/* object to be deleted */
144
		object    obj;	/* object to be deleted */
145
		delnode * next;	/* next object in the list */
145
		delnode * next;	/* next object in the list */
146
		delnode * prev; /* previous object in the list */
146
		delnode * prev; /* previous object in the list */
147
	};
147
	};
148
 
148
 
149
	static delnode * del_base = NULL;
149
	static delnode * del_base = NULL;
150
 
150
 
151
/*
151
/*
152
 *  Reduce the reference count of an object:
152
 *  Reduce the reference count of an object:
153
 *  A by-product of this might be that the object is added
153
 *  A by-product of this might be that the object is added
154
 *  to the list of objects to be deleted. If the refcount
154
 *  to the list of objects to be deleted. If the refcount
155
 *  of an object is already at zero, the object is not
155
 *  of an object is already at zero, the object is not
156
 *  added to the list, nor is the refcount reduced any further.
156
 *  added to the list, nor is the refcount reduced any further.
157
 *  If the refcount of the object is less than zero, the
157
 *  If the refcount of the object is less than zero, the
158
 *  object is a special object which cannot be deleted under
158
 *  object is a special object which cannot be deleted under
159
 *  any circumstances (e.g. default fonts and cursors).
159
 *  any circumstances (e.g. default fonts and cursors).
160
 */
160
 */
161
PROTECTED
161
PROTECTED
162
void decrease_refcount(object obj)
162
void decrease_refcount(object obj)
163
{
163
{
164
	delnode *new_node;
164
	delnode *new_node;
165
 
165
 
166
	if (! obj)
166
	if (! obj)
167
		return;
167
		return;
168
	if (obj->refcount <= 0)
168
	if (obj->refcount <= 0)
169
		return;	/* cannot delete this object */
169
		return;	/* cannot delete this object */
170
	obj->refcount--;
170
	obj->refcount--;
171
	if (obj->refcount != 0)
171
	if (obj->refcount != 0)
172
		return;	/* don't delete this object */
172
		return;	/* don't delete this object */
173
 
173
 
174
	/* the refcount must now be zero, so add to list */
174
	/* the refcount must now be zero, so add to list */
175
	new_node = create(delnode);
175
	new_node = create(delnode);
176
	new_node->obj = obj;
176
	new_node->obj = obj;
177
	if (del_base) {
177
	if (del_base) {
178
		/* add to end of circular list */
178
		/* add to end of circular list */
179
		new_node->prev = del_base->prev;
179
		new_node->prev = del_base->prev;
180
		new_node->next = del_base;
180
		new_node->next = del_base;
181
		new_node->prev->next = new_node;
181
		new_node->prev->next = new_node;
182
		new_node->next->prev = new_node;
182
		new_node->next->prev = new_node;
183
	}
183
	}
184
	else {
184
	else {
185
		/* create new circular list */
185
		/* create new circular list */
186
		new_node->next = new_node->prev = new_node;
186
		new_node->next = new_node->prev = new_node;
187
		del_base = new_node;
187
		del_base = new_node;
188
	}
188
	}
189
}
189
}
190
 
190
 
191
/*
191
/*
192
 *  Increase the reference count of an object.
192
 *  Increase the reference count of an object.
193
 *  Avoids increases a negative number (they're special).
193
 *  Avoids increases a negative number (they're special).
194
 */
194
 */
195
PROTECTED
195
PROTECTED
196
void increase_refcount(object obj)
196
void increase_refcount(object obj)
197
{
197
{
198
	if (obj && (obj->refcount >= 0))
198
	if (obj && (obj->refcount >= 0))
199
		obj->refcount ++;
199
		obj->refcount ++;
200
}
200
}
201
 
201
 
202
/*
202
/*
203
 *  Protect an object from being deleted. This function is
203
 *  Protect an object from being deleted. This function is
204
 *  used to protect default fonts and cursors from deletion.
204
 *  used to protect default fonts and cursors from deletion.
205
 *  It achieves this by simply setting the refcount to the
205
 *  It achieves this by simply setting the refcount to the
206
 *  special value -1.
206
 *  special value -1.
207
 */
207
 */
208
PROTECTED
208
PROTECTED
209
void protect_object(object obj)
209
void protect_object(object obj)
210
{
210
{
211
	if (obj)
211
	if (obj)
212
		obj->refcount = -1;
212
		obj->refcount = -1;
213
}
213
}
214
 
214
 
215
/*
215
/*
216
 *  Remove a delnode from the deletion list.
216
 *  Remove a delnode from the deletion list.
217
 *  Fix del_base to point correctly.
217
 *  Fix del_base to point correctly.
218
 */
218
 */
219
static void remove_delnode(delnode *n)
219
static void remove_delnode(delnode *n)
220
{
220
{
221
	/* remove and destroy this node */
221
	/* remove and destroy this node */
222
	n->prev->next = n->next;
222
	n->prev->next = n->next;
223
	n->next->prev = n->prev;
223
	n->next->prev = n->prev;
224
	if (n == n->next) /* nothing in list */
224
	if (n == n->next) /* nothing in list */
225
		del_base = NULL;
225
		del_base = NULL;
226
	else if (n == del_base)	/* move head */
226
	else if (n == del_base)	/* move head */
227
		del_base = n->next;
227
		del_base = n->next;
228
	discard(n);
228
	discard(n);
229
}
229
}
230
 
230
 
231
/*
231
/*
232
 *  Remove all copies of an object from the deletion list
232
 *  Remove all copies of an object from the deletion list
233
 *  after that object has been deleted.
233
 *  after that object has been deleted.
234
 *  This occurs as a by-product of object deletion, and
234
 *  This occurs as a by-product of object deletion, and
235
 *  ensures that an object cannot be deleted twice.
235
 *  ensures that an object cannot be deleted twice.
236
 */
236
 */
237
static void remove_deleted_object(object obj)
237
static void remove_deleted_object(object obj)
238
{
238
{
239
	delnode *last;
239
	delnode *last;
240
	delnode *next;
240
	delnode *next;
241
	delnode *n;
241
	delnode *n;
242
 
242
 
243
	if (! del_base)
243
	if (! del_base)
244
		return;
244
		return;
245
	next = del_base;
245
	next = del_base;
246
	last = del_base->prev;
246
	last = del_base->prev;
247
	do {
247
	do {
248
		n = next;
248
		n = next;
249
		next = n->next;
249
		next = n->next;
250
		if (n->obj == obj) {
250
		if (n->obj == obj) {
251
			/* remove and destroy this node */
251
			/* remove and destroy this node */
252
			remove_delnode(n);
252
			remove_delnode(n);
253
		}
253
		}
254
	} while (n != last);
254
	} while (n != last);
255
}
255
}
256
 
256
 
257
/*
257
/*
258
 *  Traverse the deletion list and delete every object found there.
258
 *  Traverse the deletion list and delete every object found there.
259
 *  This function repeatedly deletes del_base until that value is null.
259
 *  This function repeatedly deletes del_base until that value is null.
260
 *  This traversal occurs at the end of every doevent call.
260
 *  This traversal occurs at the end of every doevent call.
261
 */
261
 */
262
PROTECTED
262
PROTECTED
263
void deletion_traversal(void)
263
void deletion_traversal(void)
264
{
264
{
265
	static int level = 0;
265
	static int level = 0;
266
	object obj;
266
	object obj;
267
	static void del_object(object obj);	/* declaration */
267
	static void del_object(object obj);	/* declaration */
268
 
268
 
269
	level++;
269
	level++;
270
	if (level == 1) {
270
	if (level == 1) {
271
		while (del_base) {
271
		while (del_base) {
272
			obj = del_base->obj;
272
			obj = del_base->obj;
273
			if (obj) {
273
			if (obj) {
274
				if (obj->refcount == 0)
274
				if (obj->refcount == 0)
275
					del_object(obj);
275
					del_object(obj);
276
				else
276
				else
277
					remove_deleted_object(obj);
277
					remove_deleted_object(obj);
278
			}
278
			}
279
		}
279
		}
280
	}
280
	}
281
	level--;
281
	level--;
282
}
282
}
283
 
283
 
284
/*
284
/*
285
 *  Keep application global variables up-to-date.
285
 *  Keep application global variables up-to-date.
286
 */
286
 */
287
static void update_app_globals(object obj)
287
static void update_app_globals(object obj)
288
{
288
{
289
	if (obj->drawstate == current)
289
	if (obj->drawstate == current)
290
		current = & app_drawstate;
290
		current = & app_drawstate;
291
	if (obj == current->dest)
291
	if (obj == current->dest)
292
		current->dest = NULL;
292
		current->dest = NULL;
293
	if (obj == current_window)
293
	if (obj == current_window)
294
		current_window = NULL;
294
		current_window = NULL;
295
	if (obj == current_menubar)
295
	if (obj == current_menubar)
296
		current_menubar = NULL;
296
		current_menubar = NULL;
297
	if (obj == current_menu)
297
	if (obj == current_menu)
298
		current_menu = NULL;
298
		current_menu = NULL;
299
	if (obj == current->crsr)
299
	if (obj == current->crsr)
300
		current->crsr = ArrowCursor;
300
		current->crsr = ArrowCursor;
301
	if (obj == current->fnt)
301
	if (obj == current->fnt)
302
		current->fnt = SystemFont;
302
		current->fnt = SystemFont;
303
}
303
}
304
 
304
 
305
/*
305
/*
306
 *  Object deletion functions:
306
 *  Object deletion functions:
307
 *
307
 *
308
 *    The free_private function destroys the operating-system
308
 *    The free_private function destroys the operating-system
309
 *    graphics handles, also calling the user-defined and
309
 *    graphics handles, also calling the user-defined and
310
 *    internally defined deletion functions. This function
310
 *    internally defined deletion functions. This function
311
 *    also removes the object from the internal deletion list.
311
 *    also removes the object from the internal deletion list.
312
 *
312
 *
313
 *    The free_object function does this too, additionally
313
 *    The free_object function does this too, additionally
314
 *    releasing the memory used by the object from the heap.
314
 *    releasing the memory used by the object from the heap.
315
 */
315
 */
316
static void free_private(object obj)
316
static void free_private(object obj)
317
{
317
{
318
#if DEBUG
318
#if DEBUG
319
	print_object_list();
319
	print_object_list();
320
#endif
320
#endif
321
	/* Remove from object hierachy. */
321
	/* Remove from object hierachy. */
322
	remove_object(obj);
322
	remove_object(obj);
323
	/* Call user destructor first. */
323
	/* Call user destructor first. */
324
	if ((obj->call) && (obj->call->die))
324
	if ((obj->call) && (obj->call->die))
325
		obj->call->die(obj);
325
		obj->call->die(obj);
326
	/* Fix application variables. */
326
	/* Fix application variables. */
327
	update_app_globals(obj);
327
	update_app_globals(obj);
328
	/* Free drawing context. */
328
	/* Free drawing context. */
329
	del_context(obj);
329
	del_context(obj);
330
	/* Then call private destructor. */
330
	/* Then call private destructor. */
331
	if (obj->die)
331
	if (obj->die)
332
		obj->die(obj);
332
		obj->die(obj);
333
	/* Remove object from deletion list. */
333
	/* Remove object from deletion list. */
334
	remove_deleted_object(obj);
334
	remove_deleted_object(obj);
335
}
335
}
336
 
336
 
337
static void free_object(object obj)
337
static void free_object(object obj)
338
{
338
{
339
	free_private(obj);
339
	free_private(obj);
340
 
340
 
341
	/* Free any extra internal info. */
341
	/* Free any extra internal info. */
342
	if (obj->drawstate)
342
	if (obj->drawstate)
343
		discard(obj->drawstate);
343
		discard(obj->drawstate);
344
	if (obj->text)
344
	if (obj->text)
345
		discard(obj->text);
345
		discard(obj->text);
346
	if (obj->call)
346
	if (obj->call)
347
		discard(obj->call);
347
		discard(obj->call);
348
	discard(obj);
348
	discard(obj);
349
}
349
}
350
 
350
 
351
/*
351
/*
352
 *  Private recursive object destructor. Deletes child objects
352
 *  Private recursive object destructor. Deletes child objects
353
 *  first and then the top-most object specified.
353
 *  first and then the top-most object specified.
354
 *  This function is called during the deletion_traversal function,
354
 *  This function is called during the deletion_traversal function,
355
 *  which is called at the end of each doevent call.
355
 *  which is called at the end of each doevent call.
356
 */
356
 */
357
static void del_object(object obj)
357
static void del_object(object obj)
358
{
358
{
359
	while (obj->child)
359
	while (obj->child)
360
		del_object(obj->child);
360
		del_object(obj->child);
361
	free_object(obj);
361
	free_object(obj);
362
}
362
}
363
 
363
 
364
/*
364
/*
365
 *  Quick destruction function when terminating application.
365
 *  Quick destruction function when terminating application.
366
 *  Make Windows happy by releasing memory which it otherwise
366
 *  Make Windows happy by releasing memory which it otherwise
367
 *  would not automatically release (silly Windows).
367
 *  would not automatically release (silly Windows).
368
 *  Leave application variables on the heap (by not calling
368
 *  Leave application variables on the heap (by not calling
369
 *  del_object or free_object). This works fine, and avoids
369
 *  del_object or free_object). This works fine, and avoids
370
 *  problems when calling exitapp() in callbacks.
370
 *  problems when calling exitapp() in callbacks.
371
 */
371
 */
372
static void free_memory(object obj)
372
static void free_memory(object obj)
373
{
373
{
374
	if (obj->kind == WindowObject)
374
	if (obj->kind == WindowObject)
375
		hide(obj);
375
		hide(obj);
376
	while(obj->child)
376
	while(obj->child)
377
		free_memory(obj->child);
377
		free_memory(obj->child);
378
	free_private(obj);
378
	free_private(obj);
379
}
379
}
380
 
380
 
381
PROTECTED
381
PROTECTED
382
void finish_objects(void)
382
void finish_objects(void)
383
{
383
{
384
	free_memory(base_object);
384
	free_memory(base_object);
385
	base_object = NULL;
385
	base_object = NULL;
386
}
386
}
387
 
387
 
388
/*
388
/*
389
 *  Create and return a new object with a refcount of 1.
389
 *  Create and return a new object with a refcount of 1.
390
 *  The object is Transparent and may have some other
390
 *  The object is Transparent and may have some other
391
 *  structures associated with it (depending on its kind).
391
 *  structures associated with it (depending on its kind).
392
 */
392
 */
393
PROTECTED
393
PROTECTED
394
object new_object(int kind, HANDLE handle, object parent)
394
object new_object(int kind, HANDLE handle, object parent)
395
{
395
{
396
	object obj;
396
	object obj;
397
 
397
 
398
	/* create the object */
398
	/* create the object */
399
	obj = create(objinfo);
399
	obj = create(objinfo);
400
	if (! obj)
400
	if (! obj)
401
		return NULL;
401
		return NULL;
402
        obj->menubar = obj->popup = obj->toolbar = NULL;
402
        obj->menubar = obj->popup = obj->toolbar = NULL;
403
        obj->status[0] = '\0';
403
        obj->status[0] = '\0';
404
	if (kind & ControlObject)
404
	if (kind & ControlObject)
405
	{
405
	{
406
		obj->call = create(callinfo);
406
		obj->call = create(callinfo);
407
		if (! obj->call) {
407
		if (! obj->call) {
408
			discard(obj);
408
			discard(obj);
409
			return NULL;
409
			return NULL;
410
		}
410
		}
411
	}
411
	}
412
 
412
 
413
	obj->refcount = 1;
413
	obj->refcount = 1;
414
	obj->kind = kind;
414
	obj->kind = kind;
415
	obj->handle = handle;
415
	obj->handle = handle;
416
	obj->bg = Transparent;
416
	obj->bg = Transparent;
417
 
417
 
418
	add_object(obj, parent);
418
	add_object(obj, parent);
419
 
419
 
420
	return obj;
420
	return obj;
421
}
421
}
422
 
422
 
423
/*
423
/*
424
 *  Return the object if it matches the spec.
424
 *  Return the object if it matches the spec.
425
 */
425
 */
426
static object match_object(object obj, HANDLE handle, int id, int key)
426
static object match_object(object obj, HANDLE handle, int id, int key)
427
{
427
{
428
	if ((handle != 0) && (obj->handle == handle))
428
	if ((handle != 0) && (obj->handle == handle))
429
		return obj;
429
		return obj;
430
	if ((id != 0) && (obj->id == id))
430
	if ((id != 0) && (obj->id == id))
431
		return obj;
431
		return obj;
432
	if ((key != 0) && (obj->key == key)
432
	if ((key != 0) && (obj->key == key)
433
		&& (obj->kind == MenuitemObject))
433
		&& (obj->kind == MenuitemObject))
434
		return obj;
434
		return obj;
435
	return NULL;
435
	return NULL;
436
}
436
}
437
 
437
 
438
/*
438
/*
439
 *  Perform the multi-child tree traversal.
439
 *  Perform the multi-child tree traversal.
440
 */
440
 */
441
object tree_search(object top, HANDLE handle, int id, int key)
441
object tree_search(object top, HANDLE handle, int id, int key)
442
{
442
{
443
	object obj, first_object, found = NULL;
443
	object obj, first_object, found = NULL;
444
 
444
 
445
	if ((! top) || (! top->child))
445
	if ((! top) || (! top->child))
446
		return NULL;
446
		return NULL;
447
 
447
 
448
	first_object = top->child;
448
	first_object = top->child;
449
	obj = first_object;
449
	obj = first_object;
450
 
450
 
451
	while (obj != top)
451
	while (obj != top)
452
	{
452
	{
453
		found = match_object(obj, handle, id, key);
453
		found = match_object(obj, handle, id, key);
454
		if (found)
454
		if (found)
455
			break;
455
			break;
456
 
456
 
457
		if (obj->child)
457
		if (obj->child)
458
		{	/* object has children - descend tree */
458
		{	/* object has children - descend tree */
459
			first_object = obj->child;
459
			first_object = obj->child;
460
			obj = first_object;
460
			obj = first_object;
461
			continue;
461
			continue;
462
		}
462
		}
463
		else
463
		else
464
		{	/* object is childless - go to next object */
464
		{	/* object is childless - go to next object */
465
			obj = obj->next;
465
			obj = obj->next;
466
		}
466
		}
467
 
467
 
468
		while (obj == first_object)
468
		while (obj == first_object)
469
		{	/* back at first object in sibling list */
469
		{	/* back at first object in sibling list */
470
			/* climb the tree */
470
			/* climb the tree */
471
			obj = obj->parent;
471
			obj = obj->parent;
-
 
472
			if (obj == top)
-
 
473
				break;
472
			if (obj->parent) {
474
			if (obj->parent) {
473
				first_object = obj->parent->child;
475
				first_object = obj->parent->child;
474
				obj = obj->next;
476
				obj = obj->next;
475
			}
477
			}
476
			else
478
			else
477
				break;
479
				break;
478
		}
480
		}
479
	}
481
	}
480
 
482
 
481
	return found;
483
	return found;
482
}
484
}
483
 
485
 
484
PROTECTED
486
PROTECTED
485
object find_object(HANDLE handle, int id, int key)
487
object find_object(HANDLE handle, int id, int key)
486
{
488
{
487
	return tree_search(base_object, handle, id, key);
489
	return tree_search(base_object, handle, id, key);
488
}
490
}
489
 
491
 
490
/*
492
/*
491
 *  Write the tree structure to disk to examine it.
493
 *  Write the tree structure to disk to examine it.
492
 */
494
 */
493
#if DEBUG
495
#if DEBUG
494
static void print_drawstate(FILE *f, drawstate d, object obj)
496
static void print_drawstate(FILE *f, drawstate d, object obj)
495
{
497
{
496
	fprintf(f, "%lu: [", ((unsigned long)d));
498
	fprintf(f, "%lu: [", ((unsigned long)d));
497
	if (!d)
499
	if (!d)
498
		return;
500
		return;
499
	fprintf(f, "drawto=%s", (! d->drawing) ? "null" :
501
	fprintf(f, "drawto=%s", (! d->drawing) ? "null" :
500
		(d->drawing == obj) ? "this" : "??");
502
		(d->drawing == obj) ? "this" : "??");
501
	fprintf(f, ", 0x%8.8lX", (unsigned long)d->rgb);
503
	fprintf(f, ", 0x%8.8lX", (unsigned long)d->rgb);
502
	fprintf(f, ", %s", getname(d->font));
504
	fprintf(f, ", %s", getname(d->font));
503
	fprintf(f, ", %s", getname(d->cursor));
505
	fprintf(f, ", %s", getname(d->cursor));
504
	fprintf(f, ", (%d,%d)", d->point.x, d->point.y);
506
	fprintf(f, ", (%d,%d)", d->point.x, d->point.y);
505
	fprintf(f, ", width=%d", d->linewidth);
507
	fprintf(f, ", width=%d", d->linewidth);
506
	fprintf(f, "]");
508
	fprintf(f, "]");
507
}
509
}
508
 
510
 
509
static void print_objects(FILE *f, object obj, int indent)
511
static void print_objects(FILE *f, object obj, int indent)
510
{
512
{
511
	object child;
513
	object child;
512
	char *s;
514
	char *s;
513
	int i;
515
	int i;
514
 
516
 
515
	for (i=0; i<indent; i++)
517
	for (i=0; i<indent; i++)
516
		fprintf(f, " ");
518
		fprintf(f, " ");
517
	if (! obj) {
519
	if (! obj) {
518
		fprintf(f, "Null Object\n");
520
		fprintf(f, "Null Object\n");
519
		return;
521
		return;
520
	}
522
	}
521
	switch(obj->kind) {
523
	switch(obj->kind) {
522
	case BaseObject:	s = "BaseObject"; break;
524
	case BaseObject:	s = "BaseObject"; break;
523
	case ControlObject:	s = "ControlObject"; break;
525
	case ControlObject:	s = "ControlObject"; break;
524
	case WindowObject:	s = "WindowObject"; break;
526
	case WindowObject:	s = "WindowObject"; break;
525
	case BitmapObject:	s = "BitmapObject"; break;
527
	case BitmapObject:	s = "BitmapObject"; break;
526
	case CursorObject:	s = "CursorObject"; break;
528
	case CursorObject:	s = "CursorObject"; break;
527
	case FontObject:	s = "FontObject"; break;
529
	case FontObject:	s = "FontObject"; break;
528
	case UserObject:	s = "UserObject"; break;
530
	case UserObject:	s = "UserObject"; break;
529
	case LabelObject:	s = "LabelObject"; break;
531
	case LabelObject:	s = "LabelObject"; break;
530
	case ButtonObject:	s = "ButtonObject"; break;
532
	case ButtonObject:	s = "ButtonObject"; break;
531
	case CheckboxObject:	s = "CheckboxObject"; break;
533
	case CheckboxObject:	s = "CheckboxObject"; break;
532
	case RadioObject:	s = "RadioObject"; break;
534
	case RadioObject:	s = "RadioObject"; break;
533
	case ScrollbarObject:	s = "ScrollbarObject"; break;
535
	case ScrollbarObject:	s = "ScrollbarObject"; break;
534
	case FieldObject:	s = "FieldObject"; break;
536
	case FieldObject:	s = "FieldObject"; break;
535
	case TextboxObject:	s = "TextboxObject"; break;
537
	case TextboxObject:	s = "TextboxObject"; break;
536
	case ListboxObject:	s = "ListboxObject"; break;
538
	case ListboxObject:	s = "ListboxObject"; break;
537
	case ProgressbarObject:	s = "ProgressbarObject"; break;
539
	case ProgressbarObject:	s = "ProgressbarObject"; break;
538
	case MultilistObject:	s = "MultilistObject"; break;
540
	case MultilistObject:	s = "MultilistObject"; break;
539
	case DroplistObject:	s = "DroplistObject"; break;
541
	case DroplistObject:	s = "DroplistObject"; break;
540
	case DropfieldObject:	s = "DropfieldObject"; break;
542
	case DropfieldObject:	s = "DropfieldObject"; break;
541
	case MenubarObject:	s = "MenubarObject"; break;
543
	case MenubarObject:	s = "MenubarObject"; break;
542
	case MenuObject:	s = "MenuObject"; break;
544
	case MenuObject:	s = "MenuObject"; break;
543
	case MenuitemObject:	s = "MenuitemObject"; break;
545
	case MenuitemObject:	s = "MenuitemObject"; break;
544
	default: s = "Unknown Object???"; break;
546
	default: s = "Unknown Object???"; break;
545
	}
547
	}
546
	fprintf(f, "%s", s);
548
	fprintf(f, "%s", s);
547
	if (obj->text)
549
	if (obj->text)
548
		fprintf(f, ", text: \"%s\"", obj->text);
550
		fprintf(f, ", text: \"%s\"", obj->text);
549
	/*
551
	/*
550
	if (! obj->child)
552
	if (! obj->child)
551
		fprintf(f, ", child: null");
553
		fprintf(f, ", child: null");
552
	*/
554
	*/
553
	if (! obj->handle)
555
	if (! obj->handle)
554
		fprintf(f, ", handle: null");
556
		fprintf(f, ", handle: null");
555
	if (obj->drawstate) {
557
	if (obj->drawstate) {
556
		fprintf(f, ", drawstate ");
558
		fprintf(f, ", drawstate ");
557
		print_drawstate(f, obj->drawstate, obj);
559
		print_drawstate(f, obj->drawstate, obj);
558
	}
560
	}
559
	fprintf(f, "\n");
561
	fprintf(f, "\n");
560
 
562
 
561
	indent += 2;
563
	indent += 2;
562
	child = obj->child;
564
	child = obj->child;
563
	while (child) {
565
	while (child) {
564
		print_objects(f, child, indent); /* recursive */
566
		print_objects(f, child, indent); /* recursive */
565
		child = child->next;
567
		child = child->next;
566
		if (child == obj->child) /* if back at start of list */
568
		if (child == obj->child) /* if back at start of list */
567
			break;
569
			break;
568
	}
570
	}
569
	fflush(f);
571
	fflush(f);
570
}
572
}
571
 
573
 
572
PROTECTED
574
PROTECTED
573
void print_object_list(void)
575
void print_object_list(void)
574
{
576
{
575
	static int start_again = 1;
577
	static int start_again = 1;
576
	char *mode = "a";
578
	char *mode = "a";
577
	FILE *f;
579
	FILE *f;
578
 
580
 
579
	if (! app_initialised)
581
	if (! app_initialised)
580
		return;
582
		return;
581
	if (start_again) {
583
	if (start_again) {
582
		mode = "w";
584
		mode = "w";
583
		start_again = 0;
585
		start_again = 0;
584
	}
586
	}
585
	f = fopen("obj-list.txt", mode);
587
	f = fopen("obj-list.txt", mode);
586
 
588
 
587
	if (current) {
589
	if (current) {
588
		fprintf(f, "current drawstate ");
590
		fprintf(f, "current drawstate ");
589
		print_drawstate(f, current, NULL);
591
		print_drawstate(f, current, NULL);
590
		fprintf(f, "\n");
592
		fprintf(f, "\n");
591
	}
593
	}
592
	fprintf(f, "global drawstate ");
594
	fprintf(f, "global drawstate ");
593
	print_drawstate(f, & app_drawstate, NULL);
595
	print_drawstate(f, & app_drawstate, NULL);
594
	fprintf(f, "\n");
596
	fprintf(f, "\n");
595
 
597
 
596
	print_objects(f, base_object, 0);
598
	print_objects(f, base_object, 0);
597
	fprintf(f, "------------\n");
599
	fprintf(f, "------------\n");
598
	fclose(f);
600
	fclose(f);
599
}
601
}
600
#endif
602
#endif
601
 
603
 
602
void remove_menu_item(object obj)
604
void remove_menu_item(object obj)
603
{
605
{
604
	/* Must call private destructor first! */
606
	/* Must call private destructor first! */
605
	if (obj->die) obj->die(obj);
607
	if (obj->die) obj->die(obj);
606
	remove_object(obj);
608
	remove_object(obj);
607
	remove_deleted_object(obj);
609
	remove_deleted_object(obj);
608
}
610
}