home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Acorn User 2
/
AUCD2.iso
/
program
/
vista.arc
/
c
/
menu
< prev
next >
Wrap
Text File
|
1996-02-01
|
18KB
|
757 lines
// **************************************************************************
// Copyright 1996 David Allison
//
// VV VV IIIIII SSSSS TTTTTT AA
// VV VV II SS TT AA AA
// VV VV II SSSS TT AA AA
// VV VV II SS TT AAAAAAAA
// VV IIIIII SSSS TT AA AA
//
// MULTI-THREADED C++ WIMP CLASS LIBRARY
// for RISC OS
// **************************************************************************
//
// P U B L I C D O M A I N L I C E N C E
// -------------------------------------------
//
// This library is copyright. You may not sell the library for
// profit, but you may sell products which use it providing
// those products are presented as executable code and are not
// libraries themselves. The library is supplied without any
// warranty and the copyright owner cannot be held responsible for
// damage resulting from failure of any part of this library.
//
// See the User Manual for details of the licence.
//
// *************************************************************************
//
// menu.c
//
#include "Vista:icon.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <new.h>
#include <ctype.h>
#include <swis.h>
#include <kernel.h>
#include <stdarg.h>
#ifndef __EASY_C
// an exception here needs to close the file pointer used for the parser. This
// trick redefines the throw macro to close the file and then call __throw
static FILE *menu_fp ;
#undef throw
#define throw menu_fp?fclose(menu_fp):0,__throw
#endif
MenuItem::MenuItem (char *n, Menu *m, char *text, int length)
{
name = n ;
next = NULL ;
menu = m ;
submenu = NULL ;
index = m->add_item(this) ; // add a new menu item
MenuItemData *item = &m->data->items[index] ;
item->item_flags = 0 ;
item->window_handle = -1 ;
item->icon_flags = Icon::ITEXT + Icon::IFILLED + Icon::IVCENTRE + 7*Icon::IFORECOL ;
print (text) ;
}
MenuItem::MenuItem (char *name, Menu *menu, char *sprite, void *sprite_area)
{
}
void MenuItem::tick()
{
menu->data->items[index].item_flags |= TICKED ;
}
void MenuItem::untick()
{
menu->data->items[index].item_flags &= ~TICKED ;
}
void MenuItem::fade()
{
menu->data->items[index].icon_flags |= Icon::INOSELECT ;
}
void MenuItem::unfade()
{
menu->data->items[index].icon_flags &= ~Icon::INOSELECT ;
}
int MenuItem::flags(int f, int m)
{
int old = menu->data->items[index].item_flags ;
menu->data->items[index].item_flags = (old & ~m) | f ;
return old ;
}
int MenuItem::is_ticked()
{
return menu->data->items[index].item_flags & TICKED ;
}
int MenuItem::is_faded()
{
return menu->data->items[index].icon_flags & Icon::INOSELECT ;
}
MenuItem::operator int()
{
return index ;
}
MenuItem::operator char *()
{
return name ;
}
void MenuItem::make_writeable (char *buffer, int length, char *valid)
{
MenuItemData *i = &menu->data->items[index] ;
i->icon_data.indirecttext.buffer = buffer ;
i->icon_data.indirecttext.bufflen = length ;
i->icon_data.indirecttext.validstring = valid ;
i->icon_flags |= Icon::BWRITABLE * Icon::IBTYPE + Icon::INDIRECT +
Icon::IHCENTRE + Icon::IVCENTRE + Icon::ITEXT ;
i->item_flags |= WRITEABLE ;
}
void MenuItem::set_submenu(Menu *submenu)
{
this->submenu = submenu ;
menu->data->items[index].submenu = submenu->data ;
}
void Menu::init (char *n, char *title)
{
name = n ;
next = NULL ;
num_items = 0 ;
max_items = 5 ;
item_adjust = 0 ;
if ((data = (MenuData*)malloc (sizeof (MenuData) + sizeof (MenuItemData) * max_items)) == NULL)
throw ("Out of memory") ;
items = NULL ;
max_width = 0 ;
strncpy (data->title, title, 12) ;
data->title_fore_colour = 7 ;
data->title_back_colour = 2 ;
data->work_fore_colour = 7 ;
data->work_back_colour = 0 ;
data->item_width = strlen(title) * 16;
data->item_height = 44 ;
data->item_gap = 0 ;
}
Menu::Menu (char *name, char *title)
{
init (name, title) ;
}
Menu::Menu (char *name, char *title, MenuItem *item)
{
init (name, title) ;
item->set_submenu (this) ;
}
Menu::~Menu()
{
}
int Menu::add_item (MenuItem *item)
{
if (num_items == max_items) // out of item space?
{
max_items += 5 ;
if ((data = (MenuData*)realloc (data, sizeof (MenuData) + sizeof (MenuItemData) * max_items)) == NULL)
throw ("Out of memory") ;
}
item->next = items ; // add to list
items = item ;
return num_items++ ;
}
void Menu::finish()
{
items->flags(MenuItem::LAST) ; // set last item
}
void Menu::open (int x, int y)
{
_kernel_swi_regs r ;
_kernel_oserror *e ;
r.r[1] = (int)data ;
r.r[2] = x ;
r.r[3] = y ;
if ((e = _kernel_swi (Wimp_CreateMenu, &r, &r)) != NULL)
throw (e) ;
}
void Menu::close()
{
_kernel_swi_regs r ;
_kernel_oserror *e ;
r.r[1] = -1 ;
if ((e = _kernel_swi (Wimp_CreateMenu, &r, &r)) != NULL)
throw (e) ;
}
void Menu::iconbar_adjust (int &x, int &y)
{
x -= 16 ;
y = 96 ;
y += num_items * (data->item_height + data->item_gap) + item_adjust ;
}
MenuItem *Menu::selection (int *hits)
{
int i ;
MenuItem *item ;
Menu *m = this ;
for (i = 0 ; ; i++)
if (hits[i] == -1)
break ;
MenuItem *out_items = (MenuItem*)malloc ((i+1) * sizeof (MenuItem)) ;
if (out_items == NULL)
throw ("Out of memory") ;
for (i = 0 ; hits[i] != -1 ; i++)
for (item = m->items ; item != NULL ; item = item->next)
if (item->index == hits[i])
{
out_items[i] = *item ;
if (hits[i+1] != -1)
m = item->submenu ;
break ;
}
out_items[i].name = NULL ;
out_items[i].index = -1 ;
return out_items ;
}
bool Menu::invalid_selection (int *hits)
{
int i ;
MenuItem *item ;
MenuItem *last_item = NULL ;
Menu *m = this ;
for (i = 0 ; hits[i] != -1 ; i++)
for (item = m->items ; item != NULL ; item = item->next)
if (item->index == hits[i])
{
last_item = item ;
if (hits[i+1] != -1)
m = item->submenu ;
break ;
}
#ifdef __EASY_C
return last_item != NULL && (last_item->flags() & MenuItem::MENUWARNING) ;
#else
return (bool)(last_item != NULL && (last_item->flags() & MenuItem::MENUWARNING)) ;
#endif
}
MenuItem *Menu::item (char *iname)
{
for (MenuItem *item = items ; item != NULL ; item = item->next)
if (strcmp (item->name, iname) == 0)
return item ;
return NULL ;
}
void Menu::print (char *item_name, char *format ...)
{
MenuItem *i = item (item_name) ;
if (i == NULL)
throw ("No such menu item") ;
va_list ap ;
va_start (ap, format) ;
i->vprint (format, ap) ;
}
void MenuItem::print (char *format...)
{
char str[1024] ;
va_list arg ;
va_start (arg,format) ;
vprint (format, arg) ;
}
void MenuItem::vprint (char *format, va_list ap)
{
char str[1024] ;
vsprintf (str,format,ap) ;
int length = strlen (str) ;
MenuItemData *item = &menu->data->items[index] ;
if (length > menu->max_width)
{
menu->max_width = length ;
int l = (length + 1) * 16 ;
if (l > menu->data->item_width)
menu->data->item_width = l ;
}
if (length <= 12)
{
item->icon_flags &= ~Icon::INDIRECT ;
strncpy (item->icon_data.text, str, length) ;
if (length < 12)
item->icon_data.text[length] = 0 ;
}
else
{
item->icon_flags |= Icon::INDIRECT ;
item->icon_data.indirecttext.buffer = new char [length+1] ;
item->icon_data.indirecttext.validstring = (char *)-1 ;
item->icon_data.indirecttext.bufflen = length+1 ;
strcpy (item->icon_data.indirecttext.buffer, str) ;
}
}
void Menu::read (char *item_name, int &i)
{
MenuItem *it = item(item_name) ;
if (it == NULL)
throw ("No such menu item") ;
it->read (i) ;
}
void MenuItem::read (int &i)
{
char str[1024] ;
read (str) ;
sscanf (str,"%d",&i) ;
}
void Menu::read (char *item_name, char *s)
{
MenuItem *i = item(item_name) ;
if (i == NULL)
throw ("No such menu item") ;
i->read (s) ;
}
void MenuItem::read (char *s)
{
MenuItemData *item = &menu->data->items[index] ;
if (item->icon_flags & Icon::INDIRECT)
strcpy (s, item->icon_data.indirecttext.buffer) ;
else
for (int i = 0 ; i < 12 ; i++)
s[i] = item->icon_data.text[i] ;
}
//
// menu file parser
//
// tokens recognised
#define T_LBRACK 1
#define T_RBRACK 2
#define T_IDENTIFIER 3
#define T_OTHER 4
#define T_EQUALS 5
#define T_STRING 6
#define T_SEMICOLON 7
#define T_COMMA 8
#define T_COLON 9
#define T_LBRACE 10
#define T_RBRACE 11
#define T_MENU 12
#define T_SEPARATOR 13
#define T_TICKED 14
#define T_WRITEABLE 15
#define T_MESSAGE 16
#define T_OPEN 17
#define T_WINDOW 18
#define T_NUMBER 19
static char spelling[256] ;
static char input[1024] ;
static char *ch ;
static int value ;
static FILE *fp ;
static int reserved_word() ;
static void read_line()
{
for (;;)
{
if (fgets (input, 1024, fp) == NULL)
{
input[0] = '$' ;
ch = input ;
break ;
}
else
{
ch = input ;
while (isspace (*ch) && *ch != 0)
ch++ ;
if (*ch == '#' || *ch == 0)
continue ;
else
break ;
}
}
}
static int next_char()
{
if (*ch == 0)
read_line() ;
return *ch++ ;
}
static int next_token (void)
{
int c;
char str[256] ;
char *ptr ;
while (isspace (*ch)) next_char() ;
switch (c = next_char())
{
case ':':
return T_COLON ;
case ';':
return T_SEMICOLON ;
case '=':
return T_EQUALS ;
case ',':
return T_COMMA ;
case '{':
return T_LBRACE ;
case '}':
return T_RBRACE ;
case '(':
return T_LBRACK ;
case ')':
return T_RBRACK ;
default:
if (isdigit(c))
{
int i = 0 ;
do
{
spelling[i] = c ;
c = next_char() ;
i++ ;
}
while (isdigit(c)) ;
spelling[i] = 0 ;
ch-- ;
sscanf (spelling, "%d", &value) ;
return T_NUMBER ;
}
else if (isalpha (c) || c == '_')
{
ptr = spelling ;
do
{
*ptr++ = c ;
c = next_char() ;
}
while (isalnum (c) || c == '_') ;
*ptr = '\0' ;
ch-- ;
if ((c = reserved_word()) != 0)
return c ;
return T_IDENTIFIER ;
}
else if (c == '"')
{
ptr = spelling ;
while ((c = next_char()) != '"')
{
if (c == 0)
throw ("Bad string") ;
if (c == '\\')
switch (c = next_char())
{
case 'n':
*ptr++ = '\n' ;
continue ;
case 'r':
*ptr++ = '\r' ;
continue ;
case 't':
*ptr++ = '\t' ;
continue ;
case 'b':
*ptr++ = '\b' ;
continue ;
case 'f':
*ptr++ = '\f' ;
continue ;
case 'a':
*ptr++ = '\a' ;
continue ;
case 'v':
*ptr++ = '\v' ;
continue ;
default:
*ptr++ = c ;
continue ;
}
else
*ptr++ = c ;
}
*ptr = 0 ;
return T_STRING ;
}
else
return T_OTHER ;
}
}
int this_symbol ;
static void next_symbol()
{
this_symbol = next_token() ;
}
static struct
{
char *word ;
int value ;
} res_words[] = {
{"menu", T_MENU},
{"ticked", T_TICKED},
{"writeable", T_WRITEABLE},
{"message", T_MESSAGE},
{"open", T_OPEN},
{"separator", T_SEPARATOR},
{"window", T_WINDOW},
{NULL, 0}
} ;
static int reserved_word()
{
int i ;
for (i = 0 ; res_words[i].word != NULL ; i++)
if (strcmp (res_words[i].word, spelling) == 0)
return res_words[i].value ;
return 0 ;
}
static void accept (int token, char *error = "Syntax error")
{
if (this_symbol == token)
next_symbol() ;
else
throw (error) ;
}
static char *stralloc (char *name)
{
char *p = new char [strlen(name) + 1] ;
strcpy (p, name) ;
return p ;
}
static char *accept_name (int token, char *error = "Expected name")
{
char *s ;
if (this_symbol == token)
{
s = stralloc (spelling) ;
next_symbol() ;
return s ;
}
else
throw (error) ;
}
void MenuSet::parse_menu()
{
char *mname ;
char *title ;
Menu *menu ;
mname = accept_name (T_IDENTIFIER, "Menu name expected") ;
title = accept_name (T_STRING, "Missing menu title") ;
accept (T_EQUALS, "Missing =") ;
menu = new Menu (mname, title) ;
add_menu (menu) ;
parse_menu_details (menu) ; // at end this_symbol == }
menu->finish() ;
}
void MenuSet::parse_menu_details(Menu *menu)
{
accept (T_LBRACE, "Missing {") ;
while (this_symbol != T_RBRACE)
parse_item (menu) ;
}
void MenuSet::parse_item (Menu *menu)
{
char *name;
char *text ;
char *sprite ;
int has_submenu = 0 ;
MenuItem *item ;
name = accept_name (T_IDENTIFIER, "Missing item name") ;
switch (this_symbol)
{
case T_STRING:
text = stralloc (spelling) ;
item = new MenuItem (name, menu, text, strlen (text)) ;
next_symbol() ;
break ;
case T_IDENTIFIER: // sprite
default:
throw ("Syntax error") ;
}
if (this_symbol == T_COLON)
{
next_symbol() ;
for (;;)
{
switch (this_symbol)
{
case T_TICKED:
item->tick() ;
break ;
case T_WRITEABLE:
{
next_symbol() ;
accept (T_LBRACK, "Missing (") ;
accept (T_NUMBER, "Missing writeable buffer size") ;
accept (T_COMMA, "Missing ,") ;
char *valid = accept_name (T_STRING, "Missing validation string") ;
if (this_symbol != T_RBRACK)
throw ("Missing )") ;
char *s = new char [value] ;
s[0] = 0 ;
item->make_writeable (s, value, valid) ;
break;
}
case T_SEPARATOR:
item->flags (MenuItem::SEPARATOR) ;
menu->item_adjust += 24 ; // add separator adjust
break ;
case T_MESSAGE:
item->flags (MenuItem::MENUWARNING) ;
if (has_submenu)
throw ("Submenu already exists") ;
has_submenu = 1 ;
item->set_submenu ((Menu*)1) ;
break ;
case T_OPEN:
item->flags (MenuItem::OPENANYWAY) ;
break ;
case T_MENU:
if (has_submenu)
throw ("Submenu already exists") ;
has_submenu = 1 ;
next_symbol() ;
switch (this_symbol)
{
case T_IDENTIFIER:
{
Menu *m = find (spelling) ;
if (m == 0)
throw ("Unknown menu") ;
item->set_submenu (m) ;
break ;
}
case T_STRING:
{
char *title = stralloc (spelling) ;
Menu *submenu = new Menu ("", title, item) ;
next_symbol() ;
parse_menu_details (submenu) ;
submenu->finish() ;
break ;
}
default:
throw ("Syntax error") ;
}
break ;
case T_WINDOW:
break ;
}
next_symbol() ;
if (this_symbol != T_COMMA)
break ;
next_symbol() ;
}
}
accept (T_SEMICOLON, "Missing ;") ;
}
void MenuSet::add_menu(Menu *menu)
{
menu->next = menus ;
menus = menu ;
}
Menu *MenuSet::find (char *name)
{
Menu *m ;
for (m = menus ; m != NULL ; m = m->next)
if (strcmp (name, m->name) == 0)
return m ;
return NULL ;
}
MenuSet::MenuSet(Task *t, char *filename)
{
menus = NULL ; // no menu yet
if ((fp = fopen (filename, "r")) == NULL)
#ifdef __EASY_C
throw "Cant open Menu file" ;
#else
__throw ("Cant open Menu file") ;
#endif
#ifdef __EASY_C
try
#endif
{
#ifndef __EASY_C
menu_fp = fp ;
#endif
task = t ;
read_line() ;
next_symbol() ;
while (this_symbol == T_MENU)
{
next_symbol() ;
parse_menu() ;
next_symbol() ;
}
fclose (fp) ;
}
#ifdef __EASY_C
catch (char *t)
{
fclose (fp) ;
throw ; // rethrow exception
}
#endif
}
MenuSet::~MenuSet()
{
}