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