The R Project SVN R

Rev

Rev 44003 | Details | Compare with Previous | 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
{
45017 ripley 52
    if (base_object)
53
	return;
4394 ripley 54
 
45017 ripley 55
    base_object = create(objinfo);
4394 ripley 56
 
45017 ripley 57
    if (! base_object)
58
	apperror("Out of memory (init_objects)!");
4394 ripley 59
 
45017 ripley 60
    base_object->kind = BaseObject;
61
    base_object->next = base_object->prev = base_object;
62
    base_object->parent = base_object->child = NULL;
4394 ripley 63
}
64
 
65
static void add_object(object obj, object parent)
66
{
45017 ripley 67
    /* All objects have a parent. */
68
    if (! parent)
69
	parent = base_object;
4394 ripley 70
 
45017 ripley 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;
4394 ripley 83
 
84
#if DEBUG
45017 ripley 85
    print_object_list();
4394 ripley 86
#endif
87
}
88
 
89
static void remove_object(object obj)
90
{
45017 ripley 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
    }
4394 ripley 97
 
45017 ripley 98
    if (obj->parent)
99
	if (obj->parent->child == obj)
100
	    obj->parent->child = obj->next;
4394 ripley 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
{
45017 ripley 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. */
4394 ripley 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
{
45017 ripley 124
    object start = first->parent->child;
125
    object obj = first;
4394 ripley 126
 
45017 ripley 127
    do {
128
	fn(obj);
129
	obj = obj->next;
130
    } while (obj != start);
4394 ripley 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
 
45017 ripley 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
};
4394 ripley 148
 
45017 ripley 149
static delnode * del_base = NULL;
4394 ripley 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
{
45017 ripley 164
    delnode *new_node;
4394 ripley 165
 
45017 ripley 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 */
4394 ripley 173
 
45017 ripley 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
    }
4394 ripley 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
{
45017 ripley 198
    if (obj && (obj->refcount >= 0))
199
	obj->refcount ++;
4394 ripley 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
{
45017 ripley 211
    if (obj)
212
	obj->refcount = -1;
4394 ripley 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
{
45017 ripley 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);
4394 ripley 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
{
45017 ripley 239
    delnode *last;
240
    delnode *next;
241
    delnode *n;
4394 ripley 242
 
45017 ripley 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);
4394 ripley 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
 */
36841 ripley 262
static void del_object(object obj);	/* declaration */
4394 ripley 263
PROTECTED
264
void deletion_traversal(void)
265
{
45017 ripley 266
    static int level = 0;
267
    object obj;
4394 ripley 268
 
45017 ripley 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
	    }
4394 ripley 279
	}
45017 ripley 280
    }
281
    level--;
4394 ripley 282
}
283
 
284
/*
285
 *  Keep application global variables up-to-date.
286
 */
287
static void update_app_globals(object obj)
288
{
45017 ripley 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;
4394 ripley 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
45017 ripley 319
    print_object_list();
4394 ripley 320
#endif
45017 ripley 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);
4394 ripley 335
}
336
 
337
static void free_object(object obj)
338
{
45017 ripley 339
    free_private(obj);
4394 ripley 340
 
45017 ripley 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);
4394 ripley 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
{
45017 ripley 359
    while (obj->child)
360
	del_object(obj->child);
361
    free_object(obj);
4394 ripley 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
{
45017 ripley 374
    if (obj->kind == WindowObject)
375
	hide(obj);
376
    while(obj->child)
377
	free_memory(obj->child);
378
    free_private(obj);
4394 ripley 379
}
380
 
381
PROTECTED
382
void finish_objects(void)
383
{
45017 ripley 384
    free_memory(base_object);
385
    base_object = NULL;
4394 ripley 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
{
45017 ripley 396
    object obj;
4394 ripley 397
 
45017 ripley 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;
4394 ripley 410
	}
45017 ripley 411
    }
4394 ripley 412
 
45017 ripley 413
    obj->refcount = 1;
414
    obj->kind = kind;
415
    obj->handle = handle;
416
    obj->bg = Transparent;
4394 ripley 417
 
45017 ripley 418
    add_object(obj, parent);
4394 ripley 419
 
45017 ripley 420
    return obj;
4394 ripley 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
{
45017 ripley 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;
4394 ripley 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
{
45017 ripley 443
    object obj, first_object, found = NULL;
4394 ripley 444
 
45017 ripley 445
    if ((! top) || (! top->child))
446
	return NULL;
4394 ripley 447
 
45017 ripley 448
    first_object = top->child;
449
    obj = first_object;
4394 ripley 450
 
45017 ripley 451
    while (obj != top)
452
    {
453
	found = match_object(obj, handle, id, key);
454
	if (found)
455
	    break;
4394 ripley 456
 
45017 ripley 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
	}
4394 ripley 467
 
45017 ripley 468
	while (obj == first_object)
469
	{	/* back at first object in sibling list */
470
	    /* climb the tree */
471
	    obj = obj->parent;
472
	    if (obj == top)
473
		break;
474
	    if (obj->parent) {
475
		first_object = obj->parent->child;
476
		obj = obj->next;
477
	    }
478
	    else
479
		break;
4394 ripley 480
	}
45017 ripley 481
    }
4394 ripley 482
 
45017 ripley 483
    return found;
4394 ripley 484
}
485
 
486
PROTECTED
487
object find_object(HANDLE handle, int id, int key)
488
{
45017 ripley 489
    return tree_search(base_object, handle, id, key);
4394 ripley 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
{
45017 ripley 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, "]");
4394 ripley 509
}
510
 
511
static void print_objects(FILE *f, object obj, int indent)
512
{
45017 ripley 513
    object child;
514
    char *s;
515
    int i;
4394 ripley 516
 
45017 ripley 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;
539
    case ProgressbarObject:	s = "ProgressbarObject"; break;
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");
4394 ripley 562
 
45017 ripley 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);
4394 ripley 572
}
573
 
574
PROTECTED
575
void print_object_list(void)
576
{
45017 ripley 577
    static int start_again = 1;
578
    char *mode = "at"; /* to ensure text mode */
579
    FILE *f;
4394 ripley 580
 
45017 ripley 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);
4394 ripley 588
 
45017 ripley 589
    if (current) {
590
	fprintf(f, "current drawstate ");
591
	print_drawstate(f, current, NULL);
4394 ripley 592
	fprintf(f, "\n");
45017 ripley 593
    }
594
    fprintf(f, "global drawstate ");
595
    print_drawstate(f, & app_drawstate, NULL);
596
    fprintf(f, "\n");
4394 ripley 597
 
45017 ripley 598
    print_objects(f, base_object, 0);
599
    fprintf(f, "------------\n");
600
    fclose(f);
4394 ripley 601
}
602
#endif
7712 ripley 603
 
604
void remove_menu_item(object obj)
605
{
45017 ripley 606
    /* Must call private destructor first! */
607
    if (obj->die) obj->die(obj);
608
    remove_object(obj);
609
    remove_deleted_object(obj);
7712 ripley 610
}