1 /* $Id: ttymenu.c,v 2.2 2000/10/11 13:38:59 amura Exp $ */
2 /*
3  * ttymenu.c
4  *   Amiga intuition menu handling routine for Ng 1.x
5  *
6  * Copyright (C) 2000, MURAMATSU Atsushi  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY "MURAMATSU Atsushi" AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
21  * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 /*
32  * $Log: ttymenu.c,v $
33  * Revision 2.2  2000/10/11 13:38:59  amura
34  * change wildcard in ASL requester
35  *
36  * Revision 2.1  2000/10/02 14:03:14  amura
37  * rewrite from scratch
38  *
39  */
40 
41 #include "config.h"
42 #ifdef	DO_MENU
43 #include "def.h"
44 
45 #include <exec/types.h>
46 #include <exec/memory.h>
47 #include <intuition/intuition.h>
48 #include <intuition/intuitionbase.h>
49 #include <graphics/gfxbase.h>
50 #include <clib/exec_protos.h>
51 #include <clib/intuition_protos.h>
52 
53 #define	MENU_SUB_ICON	" \273"
54 #include "menumap.h"
55 
56 struct Menu *    InitEmacsMenu(struct Window *);
57 VOID             DisposeMenus(struct Menu *);
58 struct MenuItem *MakeMenuItems(MenuMap *, struct TextAttr *);
59 VOID             DisposeMenuItems(struct MenuItem *);
60 int amigamenu pro((int, int));
61 
62 /*
63  * internal function prototypes
64  * these functions based on menulayout.c included
65  *    in "AMIGA DEVELOPER CD 1.2"
66  */
67 static USHORT MaxLength(struct RastPort *textRPort,
68 			struct MenuItem *first_item, USHORT char_size);
69 static VOID adjustItems(struct RastPort *textRPort,
70 			struct MenuItem *first_item, USHORT char_size,
71 			USHORT height, USHORT level, USHORT left_edge);
72 static VOID adjustMenus(struct Menu *first_menu, struct Window *win);
73 
74 struct Menu *
InitEmacsMenu(win)75 InitEmacsMenu(win)
76 struct Window *win;
77 {
78     int  i, num;
79     struct Menu *menu;
80 
81     num = sizeof(MgMenus)/sizeof(MenuMap) - 1;
82 
83     menu = (struct Menu *)AllocMem(num*sizeof(struct Menu),
84 				   MEMF_PUBLIC|MEMF_CLEAR);
85     if (menu == NULL)
86 	return NULL;
87 
88     for (i=0; i<num; i++)
89     {
90 	menu[i].NextMenu = &menu[i+1];
91 	menu[i].Flags    = MENUENABLED;
92 	menu[i].MenuName = MgMenus[i].face;
93 	menu[i].FirstItem = (MgMenus[i].type==MENU_SUB) ?
94 	    MakeMenuItems((MenuMap*)MgMenus[i].func, win->WScreen->Font):
95 	    (struct MenuItem*)MENUNULL;
96     }
97     /* Menu Last mark */
98     menu[num-1].NextMenu = NULL;
99 
100     adjustMenus(menu, win);
101     return menu;
102 }
103 
104 
105 VOID
DisposeMenus(menu)106 DisposeMenus(menu)
107 struct Menu *menu;
108 {
109     long num;
110     struct Menu *p;
111 
112     if (menu == NULL)
113 	return;
114     for (p=menu,num=0; p!=NULL; p=p->NextMenu,num++)
115 	DisposeMenuItems(p->FirstItem);
116 
117     FreeMem(menu, num*sizeof(struct Menu));
118 }
119 
120 
121 struct MenuItem *
MakeMenuItems(em,font)122 MakeMenuItems(em, font)
123 MenuMap *em;
124 struct TextAttr *font;
125 {
126     int i, num;
127     struct IntuiText *texts;
128     struct MenuItem  *items;
129     MenuMap *p;
130 
131     /* counting menu items */
132     for (p=em,num=0; p->type!=MENU_END; p++,num++);
133 
134     texts  = (struct IntuiText *)AllocMem(num*sizeof(struct IntuiText),
135 					  MEMF_PUBLIC|MEMF_CLEAR);
136     if (texts == NULL)
137 	return NULL;
138     items = (struct MenuItem *)AllocMem(num*sizeof(struct MenuItem),
139 					MEMF_PUBLIC|MEMF_CLEAR);
140     if (items == NULL)
141     {
142 	FreeMem(texts, num*sizeof(struct IntuiText));
143 	return NULL;
144     }
145 
146     for (i=0; i<num; i++)
147     {
148 	if (MENU_TYPE(em[i].type) == MENU_SUB)
149 	{
150 	    items[i].SubItem  =	MakeMenuItems(em[i].func, font);
151 	    if (items[i].SubItem == NULL)
152 	    {
153 
154 		for (i--; i>=0; i--)
155 		    DisposeMenuItems(items[i].SubItem);
156 
157 		/* Dispose now making Items */
158 		FreeMem(texts, num*sizeof(struct IntuiText));
159 		FreeMem(items, num*sizeof(struct MenuItem));
160 		return NULL;
161 	    }
162 	}
163 	else
164 	    items[i].SubItem   = (struct MenuItem*)NULL;
165 	items[i].NextItem      = &items[i+1];
166 	if (MENU_TYPE(em[i].type) == MENU_LINE)
167 	    items[i].Flags     = ITEMTEXT|ITEMENABLED|HIGHNONE;
168 	else
169 	    items[i].Flags     = ITEMTEXT|ITEMENABLED|HIGHCOMP;
170 	items[i].ItemFill  = &texts[i];
171 
172 	/* Make IntuiText for menu face */
173 	texts[i].FrontPen  = 0;
174 	texts[i].BackPen   = 1;
175 	texts[i].DrawMode  = JAM2;
176 	texts[i].LeftEdge  = 0;
177 	texts[i].TopEdge   = 1;
178 	texts[i].IText     = (UBYTE*)em[i].face;
179 	texts[i].ITextFont = font;
180     }
181     items[num-1].NextItem = NULL;	/* Menu Last mark */
182 
183     return items;
184 }
185 
186 
187 VOID
DisposeMenuItems(items)188 DisposeMenuItems(items)
189 struct MenuItem *items;
190 {
191     long num;
192     struct MenuItem *p;
193 
194     if (items==NULL || items==(struct MenuItem *)MENUNULL)
195 	return;
196 
197     /* count menu items */
198     for (p=items,num=0; p!=NULL; p=p->NextItem,num++)
199 	DisposeMenuItems(p->SubItem);
200 
201     FreeMem(items->ItemFill, num*sizeof(struct IntuiText));
202     FreeMem(items,           num*sizeof(struct MenuItem) );
203 }
204 
205 
206 
207 /* Steps thru each item to determine the maximum width of the strip */
208 static USHORT
MaxLength(struct RastPort * textRPort,struct MenuItem * first_item,USHORT char_size)209 MaxLength(struct RastPort *textRPort,
210 	  struct MenuItem *first_item, USHORT char_size)
211 {
212     USHORT maxLength;
213     USHORT total_textlen;
214     struct MenuItem  *cur_item;
215     struct IntuiText *itext;
216     USHORT extra_width;
217     USHORT maxCommCharWidth;
218     USHORT commCharWidth;
219 
220 #ifdef	notdef
221     extra_width = char_size;  /* used as padding for each item. */
222 #endif
223 
224     /*
225      * Find the maximum length of a command character, if any.
226      * If found, it will be added to the extra_width field.
227      */
228     maxCommCharWidth = 0;
229     for (cur_item=first_item; cur_item!=NULL; cur_item=cur_item->NextItem)
230     {
231 	if (cur_item->Flags & COMMSEQ)
232 	{
233 	    commCharWidth = TextLength(textRPort,&(cur_item->Command),1);
234 	    if (commCharWidth > maxCommCharWidth)
235 		maxCommCharWidth = commCharWidth;
236 	}
237     }
238 
239     /*
240      * if we found a command sequence, add it to the extra required space.
241      * Add space for the Amiga key glyph plus space for the command
242      * character.
243      *
244      * Note this only works for HIRES screens, for LORES, use LOWCOMMWIDTH.
245      */
246     if (maxCommCharWidth > 0)
247 	extra_width += maxCommCharWidth + COMMWIDTH;
248 
249     /*
250      * Find the maximum length of the menu items, given the extra width
251      * calculated above.
252      */
253     maxLength = 0;
254     for (cur_item=first_item; cur_item!=NULL; cur_item=cur_item->NextItem)
255     {
256 	itext = (struct IntuiText *)cur_item->ItemFill;
257 	total_textlen = extra_width + itext->LeftEdge +
258 	    TextLength(textRPort, itext->IText, strlen(itext->IText));
259 
260 	/* returns the greater of the two */
261 	if (total_textlen > maxLength)
262 	    maxLength = total_textlen;
263     }
264     return maxLength;
265 }
266 
267 
268 /* Adjust the MenuItems and SubItems */
269 static VOID
adjustItems(struct RastPort * textRPort,struct MenuItem * first_item,USHORT char_size,USHORT height,USHORT level,USHORT left_edge)270 adjustItems(struct RastPort *textRPort, struct MenuItem *first_item,
271 	    USHORT char_size, USHORT height,
272 	    USHORT level, USHORT left_edge)
273 {
274     register USHORT item_num;
275     struct   MenuItem *cur_item;
276     USHORT   strip_width, subitem_edge;
277 
278     if (first_item==NULL || first_item==(struct MenuItem *)MENUNULL)
279 	return;
280 
281     /* The width of this strip is the maximum length of its members. */
282     strip_width = MaxLength(textRPort, first_item, char_size);
283 
284     /* Position the items. */
285     for (cur_item=first_item,item_num=0; cur_item!=NULL;
286 	 cur_item=cur_item->NextItem,item_num++)
287     {
288 	cur_item->TopEdge  = (item_num * height) - level;
289 	cur_item->LeftEdge = left_edge;
290 	cur_item->Width    = strip_width - left_edge;
291 	cur_item->Height   = height;
292 
293 	/* place the sub_item 3/4 of the way over on the item. */
294 	subitem_edge = strip_width - (strip_width >> 2);
295 	/* subitem_edge = strip_width >> 2; */
296 
297 	adjustItems(textRPort, cur_item->SubItem,
298 		    char_size, height, 1, subitem_edge);
299     }
300 }
301 
302 
303 /*
304  * The following routines adjust an entire menu system to conform to
305  * the specified fonts' width and height.  Allows for Proportional Fonts.
306  * This is necessary for a clean look regardless of what the users
307  * preference in Fonts may be.  Using these routines, you don't need to
308  * specify TopEdge, LeftEdge, Width or Height in the MenuItem structures.
309  *
310  * NOTE that this routine does not work for menus with images, but assumes
311  * that all menu items are rendered with IntuiText.
312  *
313  * This set of routines does NOT check/correct if the menu runs off
314  * the screen due to large fonts, too many items, lo-res screen.
315  */
316 VOID
adjustMenus(struct Menu * first_menu,struct Window * win)317 adjustMenus(struct Menu *first_menu, struct Window *win)
318 {
319     struct Menu      *cur_menu;
320     USHORT            start, char_size, height;
321 
322     /* Get the Width of the Font */
323     char_size = TextLength(win->RPort, "n", 1);
324 
325     /*
326      * To prevent crowding of the Amiga key when using COMMSEQ,
327      * don't allow the items to be less than 8 pixels high.
328      * Also, add an extra pixel for inter-line spacing.
329      */
330     if (FontHeight(win) > 8)
331 	height = 1 + FontHeight(win);
332     else
333 	height = 1 + 8;
334 
335     start = 2;      /* Set Starting Pixel */
336 
337     /* Step thru the menu structure and adjust it */
338     for (cur_menu=first_menu; cur_menu!=NULL;
339 	 cur_menu=cur_menu->NextMenu)
340     {
341 	cur_menu->LeftEdge = start;
342 	cur_menu->Width = char_size +
343 	    TextLength(win->RPort, cur_menu->MenuName,
344 		       strlen(cur_menu->MenuName));
345 	cur_menu->Height = height;
346 	cur_menu->TopEdge = 0;
347 	adjustItems(win->RPort, cur_menu->FirstItem,
348 		    char_size, height, 0, 0);
349 	start += cur_menu->Width + char_size;
350     }
351 }
352 
353 #ifdef	ASL
354 #include <libraries/asl.h>
355 #include <clib/asl_protos.h>
356 
357 #define	ASL_OPEN	1
358 #define	ASL_INSERT	2
359 #define	ASL_WRITE	3
360 
361 struct Library *AslBase;
362 
363 static int
alsmenu(dir,mode,buff)364 alsmenu(dir, mode, buff)
365 char *dir;
366 char *buff;
367 {
368     struct FileRequester *fr;
369     char *text_OK, *title;
370     ULONG asl_flag;
371 
372     if (AslBase = OpenLibrary("asl.library", 0L))
373     {
374 	asl_flag = FILF_PATGAD;
375 	buff[0] = '\0';
376 	switch (mode)
377 	{
378 	  case ASL_OPEN:
379 	    text_OK = "Open";
380 	    title   = "Select File to Open";
381 	    break;
382 	  case ASL_INSERT:
383 	    text_OK = "Insert";
384 	    title   = "Select File to Insert";
385 	    break;
386 	  case ASL_WRITE:
387 	    text_OK = "Save";
388 	    title   = "Select File to Save to";
389 	    asl_flag |= FILF_SAVE;
390 	    break;
391 	  default:
392 	    text_OK = "OK";
393 	    title   = "Select File";
394 	}
395 
396 	if (fr = AllocAslRequestTags(ASL_FileRequest,
397 			ASL_Hail,	(ULONG)title,
398 			ASL_Dir,	(ULONG)dir,
399 			ASL_Pattern,	"~(#?'~)",
400 			ASL_OKText,	(ULONG)text_OK,
401 			ASL_CancelText,	(ULONG)"Cancel",
402 			ASL_FuncFlags,	asl_flag,
403 			TAG_DONE
404 				     ))
405 	{
406 	    if (AslRequest(fr, NULL))
407 	    {
408 		strncpy(buff, fr->rf_Dir, NFILEN);
409 		buff[NFILEN-1] = '\0';
410 		if (!AddPart(buff, fr->rf_File, NFILEN))
411 		    buff[0] = '\0';
412 	    }
413 	    FreeAslRequest(fr);
414 	}
415 	CloseLibrary(AslBase);
416 
417 	return TRUE;
418     }
419     return FALSE;
420 }
421 
422 int
aslopen(f,c)423 aslopen(f, c)
424 {
425     char fname[NFILEN];
426 
427     alsmenu(curbp->b_cwd, ASL_OPEN, fname);
428     eargset(fname);
429     return filevisit(f, c);
430 }
431 
432 int
aslinsert(f,c)433 aslinsert(f, c)
434 {
435     char fname[NFILEN];
436 
437     alsmenu(curbp->b_cwd, ASL_INSERT, fname);
438     eargset(fname);
439     return fileinsert(f, c);
440 }
441 
442 int
aslwrite(f,c)443 aslwrite(f, c)
444 {
445     char fname[NFILEN];
446 
447     alsmenu(curbp->b_cwd, ASL_WRITE, fname);
448     eargset(fname);
449     return filewrite(f, c);
450 }
451 #endif	/* ASL */
452 
453 int
amigamenu(f,c)454 amigamenu(f, c)
455 int f, c;
456 {
457     MenuMap *em;
458     int menuNum,itemNum,subNum;
459 
460     menuNum = getkbd() - MN_OFFSET;
461     itemNum = getkbd() - MN_OFFSET;
462     subNum  = getkbd() - MN_OFFSET;
463 
464     if (menuNum == NOITEM)
465 	return TRUE;
466     em = MgMenus;
467     switch (MENU_TYPE(em[menuNum].type))
468     {
469       case MENU_LINE:
470 	return TRUE;
471       case MENU_FUNC:
472 	return ((int (*)())em[menuNum].func)(f, c);
473       case MENU_SUB:
474 	if (itemNum == NOITEM)
475 	    return TRUE;
476 	em = (MenuMap*)(em[menuNum].func);
477 	switch (MENU_TYPE(em[itemNum].type))
478 	{
479           case MENU_LINE:
480 	    return TRUE;
481 	  case MENU_FUNC:
482 	    return ((int (*)())em[itemNum].func)(f, c);
483 	  case MENU_SUB:
484 	    if (subNum == NOITEM)
485 		return TRUE;
486 	    em = (MenuMap*)(em[itemNum].func);
487 	    switch (MENU_TYPE(em[subNum].type))
488 	    {
489 	      case MENU_LINE:
490 		return TRUE;
491               case MENU_FUNC:
492 		return ((int (*)())em[subNum].func)(f, c);
493 	    }
494 	}
495 
496     }
497 
498     ewprintf("BUGS in amiga menu execute");
499     return FALSE;
500 }
501 
502 #endif	/* DO_MENU */
503