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: menus.c -- creating menus, menubars, etc.
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: New object class system.
9
 * Version: 2.15  Changes: Added submenu creation function.
10
 * Version: 2.20  Changes: New menuitem constructor.
11
 * Version: 2.30  Changes: Modified the shortcut key selector.
12
 * Version: 2.35  Changes: New reference count 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
/*
28
 *  When you create menubars, menus and menuitems, they are added
29
 *  to the currentwindow. Menus are added to the current_menubar and
30
 *  menuitems are added to the current_menu.
31
 */
32
 
33
#include "internal.h"
34
 
35
/*
36
 *  Menu variables.
37
 */
38
 
39
	PROTECTED int     menus_active = 1;
40
 
41
	PROTECTED menubar current_menubar = NULL;
42
	PROTECTED menu    current_menu    = NULL;
43
 
44
	static    int     id = MinMenuID;
45
 
46
	PROTECTED HACCEL hAccel = 0;
47
 
48
/*
49
 *  Initialise menus. Actually, all we do here is load accelerators.
50
 *  The accelarator resource must have the name "ACCELS".
51
 *  If there are no accelerator resources, hAccel will be zero.
52
 */
53
PROTECTED
54
void init_menus(void)
55
{
56
	if (this_instance != 0)
57
		hAccel = LoadAccelerators(this_instance, "ACCELS");
58
}
59
 
60
/*
61
 *  Find the position of a menu on its parent menubar.
62
 *  Return zero for the first menu, one for the next etc.
63
 */
64
static int find_menu_position(menu parent, char *name)
65
{
66
	menu m, first;
67
	int which = 0;
68
 
69
	first = m = parent->child;
70
	if (first) {
71
		do {
7712 ripley 72
			if (strcmp(m->text, name) == 0)
4394 ripley 73
				break;
74
			which ++;
75
			m = m->next;
76
		} while (m != first);
77
	}
78
	return which;
79
}
80
 
81
/*
82
 *  Private menu deletion function.
83
 */
84
static void private_delmenu(menu m)
85
{
86
	window w = parentwindow(m);
87
	if (m->kind == MenuitemObject)
88
		RemoveMenu(m->parent->handle, m->id, MF_BYCOMMAND);
89
	else if (m->kind == MenuObject) {
90
		DeleteMenu(m->parent->handle,
91
			find_menu_position(m->parent, m->text),
92
			MF_BYPOSITION);
93
	}
94
	else
95
		DestroyMenu(m->handle);
96
	if (w)
97
		DrawMenuBar(w->handle);
98
}
99
 
100
/*
101
 *  Find a char within a string.
102
 *  Return -1 if not found, or a number from 0 to strlen(str)-1
103
 *  to indicate where the char is.
104
 */
105
static int find_char(int ch, char *str)
106
{
107
	int where;
108
 
109
	for (where=0; str[where] != '\0'; where++)
110
		if (str[where] == ch)
111
			return where;
112
	return -1;
113
}
114
 
115
/*
116
 *  This function forms a search string, to look for the best
117
 *  letter to underline in a menu item. It first tries the
118
 *  accelerator key, then the uppercase letters from the menu name,
119
 *  then the digits, and finally the lowercase letters.
120
 *  It ignores spaces.
121
 */
122
static void set_search_string(char *search, char *name, int key)
123
{
124
	int source;
125
	int dest = 0;
126
 
127
	/* handle a couple of special cases first */
128
	if (! string_diff(name, "Cut"))  search[dest++] = 't';
129
	if (! string_diff(name, "Exit")) search[dest++] = 'x';
130
	if (! string_diff(name, "?")) search[dest++] = '?';
131
 
132
	/* add the accelerator key if it is in the name string */
133
	if (key) {
134
		key = toupper(key);
135
		if (find_char(key, name) >= 0)
136
			search[dest++] = (char) key;
137
		else {
138
			key = tolower(key);
139
			if (find_char(key, name) >= 0)
140
				search[dest++] = (char) key;
141
		}
142
	}
143
	/* add the uppercase letters */
144
	for (source=0; name[source]; source++) {
145
		if (isupper(name[source]))
146
			search[dest++] = name[source];
147
	}
148
	/* add the digits */
149
	for (source=0; name[source]; source++) {
150
		if (isdigit(name[source]))
151
			search[dest++] = name[source];
152
	}
153
	/* add the lowercase letters */
154
	for (source=0; name[source]; source++) {
155
		if (islower(name[source]))
156
			search[dest++] = name[source];
157
	}
158
	/* end the search string */
159
	search[dest] = '\0';
160
}
161
 
162
/*
163
 *  Look through the list of siblings of the given object, and
164
 *  find a shortcut key from the search string which hasn't already
165
 *  been used by some other object. Return the valid shortcut char,
166
 *  or else zero if we can't find one.
167
 */
168
static int find_shortcut(object me, char *search)
169
{
170
	int source;
171
	object first, obj;
172
 
173
	first = me->parent->child;
174
 
175
	for (source = 0; search[source]; source++)
176
	{
177
		/* for each character in the search string */
178
		/* look through every sibling object */
179
 
180
		for (obj = first; obj; obj = obj->next)
181
		{
182
			if (obj == me) /* at end of list, success! */
183
				return search[source];
184
 
185
			/* use uppercase comparisons */
186
			if (obj->shortcut == toupper(search[source]))
187
				break; /* can't use this shortcut */
188
 
189
		}
190
	}	
191
 
192
	return 0;
193
}
194
 
195
/*
196
 *  Take "Open..." and 'O' and produce the string "Open...\tCtrl+O".
197
 *  This function also sets an object's shortcut key, which is the
198
 *  underlined letter in a menu item in Windows or X-Windows.
199
 */
200
static void setmenustring(object obj, char *buf, char *name, int key)
201
{
202
	char search[256];
203
	int ch, where, source, dest = 0;
204
	char *extra = "\tCtrl+";
205
 
206
	set_search_string(search, name, key);
207
	ch = find_shortcut(obj, search);
208
 
209
	if (ch) /* found a valid shortcut key */
210
	{
211
		obj->shortcut = toupper(ch); /* case-insensitive */
212
		where = find_char(ch, name);
213
 
214
		for (source=0; source < where; source++)
215
			buf[dest++] = name[source];
216
		buf[dest++] = '&';
217
		for (; name[source]; source++)
218
			buf[dest++] = name[source];
219
	}
220
	else /* no shortcut key, just copy the name string */
221
	{
222
		for (source=0; name[source]; source++)
223
			buf[dest++] = name[source];
224
	}
225
 
226
	if (key) {
227
		for (source=0; extra[source]; source++)
228
			buf[dest++] = extra[source];
229
		buf[dest++] = key;
230
	}
231
 
232
	buf[dest] = '\0';
233
}
234
 
235
/*
236
 *  Menu functions.
237
 */
238
menubar newmenubar(actionfn adjust_menus)
239
{
240
	object obj;
241
	HMENU hm;
242
 
243
	if (! current_window) {
244
		current_window = simple_window();
245
		show(current_window);
246
	}
247
 
248
	hm = CreateMenu();
249
	obj = new_object(MenubarObject, hm, current_window);
250
	if (obj) {
251
                current_window->menubar = obj;
252
                obj->menubar = NULL;
253
		obj->die = private_delmenu;
254
		obj->id = id++;
255
		obj->action = adjust_menus;
256
		obj->text = new_string("Menubar");
257
		current_menubar = obj;
258
		SetMenu(current_window->handle, hm);
259
	}
260
	return (menubar) obj;
261
}
262
 
263
 
264
menu newsubmenu(menu parent, char *name)
265
{
266
	object obj;
267
	HMENU hm;
268
	UINT flags = MF_POPUP;
269
	char str[256];
270
	if (! parent) {
271
		if (! current_menubar)
272
			current_menubar = newmenubar(NULL);
273
		parent = current_menubar;
274
	}
275
 
276
	if (! parent)
277
		return NULL;
278
	if (! name)
279
		name = "";
280
	if (name[0] == '\t') {
281
		name += 1;
282
		flags |= MF_HELP;
283
	}
284
 
285
	if (parent->kind == WindowObject) 
286
           hm = CreatePopupMenu();
287
        else
288
           hm = CreateMenu();
289
	obj = new_object(MenuObject, hm, parent);
290
	if (obj) {
291
		obj->die = private_delmenu;
292
		obj->id = id++;
293
		obj->text = new_string(name);
294
		setmenustring(obj, str, name, 0);
295
		name = str;
296
		current_menu = obj;
297
	}
298
	if (parent->kind != WindowObject) 
299
             AppendMenu(parent->handle, flags, (UINT) hm, name);
300
	if (parent == current_menubar)
301
		DrawMenuBar(current_menubar->parent->handle);
302
 
303
	return (menu) obj;
304
}
305
 
306
menu newmenu(char *name)
307
{
308
	return newsubmenu(current_menubar, name);
309
}
310
 
311
 
312
menuitem newmenuitem(char *name, int key, menufn fn)
313
{
314
	object obj;
315
	UINT flags;
316
	char str[256];
317
 
318
	if (! current_menu)
319
		current_menu = newmenu("Special");
320
	if (! current_menu)
321
		return NULL;
322
	if (! name)
323
		name = "-"; /* separator */
324
 
325
	key = toupper(key); /* make it uppercase */
326
 
327
	obj = new_object(MenuitemObject, 0, current_menu);
328
	if (obj) {
329
		obj->die = private_delmenu;
330
		obj->id = id++;
331
		obj->key = key;
332
		obj->action = fn;
333
		obj->value = 0;
334
		obj->text = new_string(name);
335
 
336
		if (name[0] == '-') {
337
			flags = MF_SEPARATOR;
338
			name = NULL;
339
		} else {
340
			flags = MF_STRING;
341
			setmenustring(obj, str, name, key);
342
			name = str;
343
		}
344
 
345
		AppendMenu(current_menu->handle, flags, obj->id, name);
346
	}
347
	return (menuitem) obj;
348
}
349
 
350
/*
351
 *  Find various parent objects of a menu (or any object).
352
 */
353
#if 0
354
PROTECTED
355
object parent_menubar(object obj)
356
{
357
	while (obj) {
358
		if (obj->kind == MenubarObject)
359
			break;
360
		obj = obj->parent;
361
	}
362
	return obj;
363
}
364
 
365
PROTECTED
366
object parent_menu(object obj)
367
{
368
	while (obj) {
369
		if (obj->kind == MenuObject)
370
			break;
371
		obj = obj->parent;
372
	}
373
	return obj;
374
}
375
#endif
376
 
377
/*
378
 *  The adjust_menu function is called just after the program
379
 *  receives a WM_INITMENU message.  This message is sent even if
380
 *  the user is attempting to use the system menu, which is nice.
381
 *  We use this function to keep menubars and menus up-to-date by
382
 *  calling their respective action functions.
383
 */
384
PROTECTED
385
void adjust_menu(WPARAM wParam)
386
{
387
	object obj;
388
	obj = find_by_handle((HANDLE)wParam);
389
	if (obj) {
390
		activatecontrol(obj);
391
		if (obj->kind == MenubarObject)
392
			DrawMenuBar(obj->parent->handle);
393
	}
394
}
395
 
396
/*
397
 *  Handle the menu selection. wParam is what WM_[SYS]COMMAND sets
398
 *  it to i.e. the object id number. The menus will already have
399
 *  received WM_INITMENU messages so they will be up-to-date.
400
 */
401
PROTECTED
402
void handle_menu_id (WPARAM wParam)
403
{
404
	object obj;
405
 
406
	obj = find_by_id(wParam);
407
	if (obj)
408
		activatecontrol(obj);
409
}
410
 
411
/*
412
 *  Handle a menu accelerator key. The key will be a normal char,
413
 *  in the range of 0..9 or A..Z (not a..z because we use KEYDOWNs).
414
 *  Uses a recursive function to call the menu adjustor functions
415
 *  in order from menubar down to popup menu.
416
 */
417
 
418
static void adjust_menus_top_down(object obj)
419
{
420
	/* Recursively step up the list. */
421
	if (! obj)
422
		return;
423
	adjust_menus_top_down(obj->parent);
424
 
425
	/* Adjust menubar then child menus as we descend. */
426
	if (obj->kind == MenubarObject) {
427
		activatecontrol(obj);
428
		DrawMenuBar(obj->parent->handle);
429
	}
430
	else if (obj->kind == MenuObject)
431
		activatecontrol(obj);
432
}
433
 
434
PROTECTED
435
void handle_menu_key(WPARAM wParam)
436
{
437
	object obj = find_by_key(wParam);
438
	if (obj) {
439
		adjust_menus_top_down(obj);
440
		activatecontrol(obj);
441
	}
442
}