The R Project SVN R

Rev

Details | Last modification | View Log | RSS feed

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