Blame | Last modification | View Log | Download | RSS feed
/** GraphApp - Cross-Platform Graphics Programming Library.** File: menus.c -- creating menus, menubars, etc.* Platform: Windows Version: 2.35 Date: 1998/04/04** Version: 1.00 Changes: Original version by Lachlan Patrick.* Version: 2.00 Changes: New object class system.* Version: 2.15 Changes: Added submenu creation function.* Version: 2.20 Changes: New menuitem constructor.* Version: 2.30 Changes: Modified the shortcut key selector.* Version: 2.35 Changes: New reference count technique.*//* Copyright (C) 1993-1998 Lachlan PatrickThis file is part of GraphApp, a cross-platform C graphics library.GraphApp is free software; you can redistribute it and/or modify itunder the terms of the GNU Library General Public License.GraphApp is distributed in the hope that it will be useful, butWITHOUT ANY WARRANTY.See the file COPYLIB.TXT for details.*//** When you create menubars, menus and menuitems, they are added* to the currentwindow. Menus are added to the current_menubar and* menuitems are added to the current_menu.*/#include "internal.h"/** Menu variables.*/PROTECTED int menus_active = 1;PROTECTED menubar current_menubar = NULL;PROTECTED menu current_menu = NULL;static int id = MinMenuID;PROTECTED HACCEL hAccel = 0;/** Initialise menus. Actually, all we do here is load accelerators.* The accelarator resource must have the name "ACCELS".* If there are no accelerator resources, hAccel will be zero.*/PROTECTEDvoid init_menus(void){if (this_instance != 0)hAccel = LoadAccelerators(this_instance, "ACCELS");}/** Find the position of a menu on its parent menubar.* Return zero for the first menu, one for the next etc.*/static int find_menu_position(menu parent, char *name){menu m, first;int which = 0;first = m = parent->child;if (first) {do {if (m->text == name)break;which ++;m = m->next;} while (m != first);}return which;}/** Private menu deletion function.*/static void private_delmenu(menu m){window w = parentwindow(m);if (m->kind == MenuitemObject)RemoveMenu(m->parent->handle, m->id, MF_BYCOMMAND);else if (m->kind == MenuObject) {DeleteMenu(m->parent->handle,find_menu_position(m->parent, m->text),MF_BYPOSITION);}elseDestroyMenu(m->handle);if (w)DrawMenuBar(w->handle);}/** Find a char within a string.* Return -1 if not found, or a number from 0 to strlen(str)-1* to indicate where the char is.*/static int find_char(int ch, char *str){int where;for (where=0; str[where] != '\0'; where++)if (str[where] == ch)return where;return -1;}/** This function forms a search string, to look for the best* letter to underline in a menu item. It first tries the* accelerator key, then the uppercase letters from the menu name,* then the digits, and finally the lowercase letters.* It ignores spaces.*/static void set_search_string(char *search, char *name, int key){int source;int dest = 0;/* handle a couple of special cases first */if (! string_diff(name, "Cut")) search[dest++] = 't';if (! string_diff(name, "Exit")) search[dest++] = 'x';if (! string_diff(name, "?")) search[dest++] = '?';/* add the accelerator key if it is in the name string */if (key) {key = toupper(key);if (find_char(key, name) >= 0)search[dest++] = (char) key;else {key = tolower(key);if (find_char(key, name) >= 0)search[dest++] = (char) key;}}/* add the uppercase letters */for (source=0; name[source]; source++) {if (isupper(name[source]))search[dest++] = name[source];}/* add the digits */for (source=0; name[source]; source++) {if (isdigit(name[source]))search[dest++] = name[source];}/* add the lowercase letters */for (source=0; name[source]; source++) {if (islower(name[source]))search[dest++] = name[source];}/* end the search string */search[dest] = '\0';}/** Look through the list of siblings of the given object, and* find a shortcut key from the search string which hasn't already* been used by some other object. Return the valid shortcut char,* or else zero if we can't find one.*/static int find_shortcut(object me, char *search){int source;object first, obj;first = me->parent->child;for (source = 0; search[source]; source++){/* for each character in the search string *//* look through every sibling object */for (obj = first; obj; obj = obj->next){if (obj == me) /* at end of list, success! */return search[source];/* use uppercase comparisons */if (obj->shortcut == toupper(search[source]))break; /* can't use this shortcut */}}return 0;}/** Take "Open..." and 'O' and produce the string "Open...\tCtrl+O".* This function also sets an object's shortcut key, which is the* underlined letter in a menu item in Windows or X-Windows.*/static void setmenustring(object obj, char *buf, char *name, int key){char search[256];int ch, where, source, dest = 0;char *extra = "\tCtrl+";set_search_string(search, name, key);ch = find_shortcut(obj, search);if (ch) /* found a valid shortcut key */{obj->shortcut = toupper(ch); /* case-insensitive */where = find_char(ch, name);for (source=0; source < where; source++)buf[dest++] = name[source];buf[dest++] = '&';for (; name[source]; source++)buf[dest++] = name[source];}else /* no shortcut key, just copy the name string */{for (source=0; name[source]; source++)buf[dest++] = name[source];}if (key) {for (source=0; extra[source]; source++)buf[dest++] = extra[source];buf[dest++] = key;}buf[dest] = '\0';}/** Menu functions.*/menubar newmenubar(actionfn adjust_menus){object obj;HMENU hm;if (! current_window) {current_window = simple_window();show(current_window);}hm = CreateMenu();obj = new_object(MenubarObject, hm, current_window);if (obj) {current_window->menubar = obj;obj->menubar = NULL;obj->die = private_delmenu;obj->id = id++;obj->action = adjust_menus;obj->text = new_string("Menubar");current_menubar = obj;SetMenu(current_window->handle, hm);}return (menubar) obj;}menu newsubmenu(menu parent, char *name){object obj;HMENU hm;UINT flags = MF_POPUP;char str[256];if (! parent) {if (! current_menubar)current_menubar = newmenubar(NULL);parent = current_menubar;}if (! parent)return NULL;if (! name)name = "";if (name[0] == '\t') {name += 1;flags |= MF_HELP;}if (parent->kind == WindowObject)hm = CreatePopupMenu();elsehm = CreateMenu();obj = new_object(MenuObject, hm, parent);if (obj) {obj->die = private_delmenu;obj->id = id++;obj->text = new_string(name);setmenustring(obj, str, name, 0);name = str;current_menu = obj;}if (parent->kind != WindowObject)AppendMenu(parent->handle, flags, (UINT) hm, name);if (parent == current_menubar)DrawMenuBar(current_menubar->parent->handle);return (menu) obj;}menu newmenu(char *name){return newsubmenu(current_menubar, name);}menuitem newmenuitem(char *name, int key, menufn fn){object obj;UINT flags;char str[256];if (! current_menu)current_menu = newmenu("Special");if (! current_menu)return NULL;if (! name)name = "-"; /* separator */key = toupper(key); /* make it uppercase */obj = new_object(MenuitemObject, 0, current_menu);if (obj) {obj->die = private_delmenu;obj->id = id++;obj->key = key;obj->action = fn;obj->value = 0;obj->text = new_string(name);if (name[0] == '-') {flags = MF_SEPARATOR;name = NULL;} else {flags = MF_STRING;setmenustring(obj, str, name, key);name = str;}AppendMenu(current_menu->handle, flags, obj->id, name);}return (menuitem) obj;}/** Find various parent objects of a menu (or any object).*/#if 0PROTECTEDobject parent_menubar(object obj){while (obj) {if (obj->kind == MenubarObject)break;obj = obj->parent;}return obj;}PROTECTEDobject parent_menu(object obj){while (obj) {if (obj->kind == MenuObject)break;obj = obj->parent;}return obj;}#endif/** The adjust_menu function is called just after the program* receives a WM_INITMENU message. This message is sent even if* the user is attempting to use the system menu, which is nice.* We use this function to keep menubars and menus up-to-date by* calling their respective action functions.*/PROTECTEDvoid adjust_menu(WPARAM wParam){object obj;obj = find_by_handle((HANDLE)wParam);if (obj) {activatecontrol(obj);if (obj->kind == MenubarObject)DrawMenuBar(obj->parent->handle);}}/** Handle the menu selection. wParam is what WM_[SYS]COMMAND sets* it to i.e. the object id number. The menus will already have* received WM_INITMENU messages so they will be up-to-date.*/PROTECTEDvoid handle_menu_id (WPARAM wParam){object obj;obj = find_by_id(wParam);if (obj)activatecontrol(obj);}/** Handle a menu accelerator key. The key will be a normal char,* in the range of 0..9 or A..Z (not a..z because we use KEYDOWNs).* Uses a recursive function to call the menu adjustor functions* in order from menubar down to popup menu.*/static void adjust_menus_top_down(object obj){/* Recursively step up the list. */if (! obj)return;adjust_menus_top_down(obj->parent);/* Adjust menubar then child menus as we descend. */if (obj->kind == MenubarObject) {activatecontrol(obj);DrawMenuBar(obj->parent->handle);}else if (obj->kind == MenuObject)activatecontrol(obj);}PROTECTEDvoid handle_menu_key(WPARAM wParam){object obj = find_by_key(wParam);if (obj) {adjust_menus_top_down(obj);activatecontrol(obj);}}