blob: 5ce264dcdb7d8441ecab360e862a9f920e29dcb3 [file] [log] [blame]
/* Copyright (c) Mark J. Kilgard, 1994, 1997, 1998. */
/* Copyright (c) Nate Robins, 1997. */
/* This program is freely distributable without licensing fees
and is provided without guarantee or warrantee expressed or
implied. This program is -not- in the public domain. */
/* This file completely re-implements glut_menu.c and glut_menu2.c
for Win32. Note that neither glut_menu.c nor glut_menu2.c are
compiled into Win32 GLUT. */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include "glutint.h"
void (GLUTCALLBACK *__glutMenuStatusFunc) (int, int, int);
GLUTmenu *__glutMappedMenu;
GLUTwindow *__glutMenuWindow;
GLUTmenuItem *__glutItemSelected;
unsigned __glutMenuButton;
static GLUTmenu **menuList = NULL;
static int menuListSize = 0;
static UINT uniqueMenuHandler = 1;
/* DEPRICATED, use glutMenuStatusFunc instead. */
void GLUTAPIENTRY
glutMenuStateFunc(GLUTmenuStateCB menuStateFunc)
{
__glutMenuStatusFunc = (GLUTmenuStatusCB) menuStateFunc;
}
void GLUTAPIENTRY
glutMenuStatusFunc(GLUTmenuStatusCB menuStatusFunc)
{
__glutMenuStatusFunc = menuStatusFunc;
}
void
__glutSetMenu(GLUTmenu * menu)
{
__glutCurrentMenu = menu;
}
static void
unmapMenu(GLUTmenu * menu)
{
if (menu->cascade) {
unmapMenu(menu->cascade);
menu->cascade = NULL;
}
menu->anchor = NULL;
menu->highlighted = NULL;
}
void
__glutFinishMenu(Window win, int x, int y)
{
unmapMenu(__glutMappedMenu);
/* XXX Put in a GdiFlush just in case. Probably unnecessary. -mjk */
GdiFlush();
if (__glutMenuStatusFunc) {
__glutSetWindow(__glutMenuWindow);
__glutSetMenu(__glutMappedMenu);
/* Setting __glutMappedMenu to NULL permits operations that
change menus or destroy the menu window again. */
__glutMappedMenu = NULL;
__glutMenuStatusFunc(GLUT_MENU_NOT_IN_USE, x, y);
}
/* Setting __glutMappedMenu to NULL permits operations that
change menus or destroy the menu window again. */
__glutMappedMenu = NULL;
/* If an item is selected and it is not a submenu trigger,
generate menu callback. */
if (__glutItemSelected && !__glutItemSelected->isTrigger) {
__glutSetWindow(__glutMenuWindow);
/* When menu callback is triggered, current menu should be
set to the callback menu. */
__glutSetMenu(__glutItemSelected->menu);
__glutItemSelected->menu->select(__glutItemSelected->value);
}
__glutMenuWindow = NULL;
}
static void
mapMenu(GLUTmenu * menu, int x, int y)
{
TrackPopupMenu((HMENU) menu->win, TPM_LEFTALIGN |
(__glutMenuButton == TPM_RIGHTBUTTON) ? TPM_RIGHTBUTTON : TPM_LEFTBUTTON,
x, y, 0, __glutCurrentWindow->win, NULL);
}
void
__glutStartMenu(GLUTmenu * menu, GLUTwindow * window,
int x, int y, int x_win, int y_win)
{
assert(__glutMappedMenu == NULL);
__glutMappedMenu = menu;
__glutMenuWindow = window;
__glutItemSelected = NULL;
if (__glutMenuStatusFunc) {
__glutSetMenu(menu);
__glutSetWindow(window);
__glutMenuStatusFunc(GLUT_MENU_IN_USE, x_win, y_win);
}
mapMenu(menu, x, y);
}
GLUTmenuItem *
__glutGetUniqueMenuItem(GLUTmenu * menu, UINT unique)
{
GLUTmenuItem *item;
int i;
i = menu->num;
item = menu->list;
while (item) {
if (item->unique == unique) {
return item;
}
if (item->isTrigger) {
GLUTmenuItem *subitem;
subitem = __glutGetUniqueMenuItem(menuList[item->value], unique);
if (subitem) {
return subitem;
}
}
i--;
item = item->next;
}
return NULL;
}
GLUTmenuItem *
__glutGetMenuItem(GLUTmenu * menu, Window win, int *which)
{
GLUTmenuItem *item;
int i;
i = menu->num;
item = menu->list;
while (item) {
if (item->win == win) {
*which = i;
return item;
}
if (item->isTrigger) {
GLUTmenuItem *subitem;
subitem = __glutGetMenuItem(menuList[item->value],
win, which);
if (subitem) {
return subitem;
}
}
i--;
item = item->next;
}
return NULL;
}
GLUTmenu *
__glutGetMenu(Window win)
{
GLUTmenu *menu;
menu = __glutMappedMenu;
while (menu) {
if (win == menu->win) {
return menu;
}
menu = menu->cascade;
}
return NULL;
}
GLUTmenu *
__glutGetMenuByNum(int menunum)
{
if (menunum < 1 || menunum > menuListSize) {
return NULL;
}
return menuList[menunum - 1];
}
static int
getUnusedMenuSlot(void)
{
int i;
/* Look for allocated, unused slot. */
for (i = 0; i < menuListSize; i++) {
if (!menuList[i]) {
return i;
}
}
/* Allocate a new slot. */
menuListSize++;
if (menuList) {
menuList = (GLUTmenu **)
realloc(menuList, menuListSize * sizeof(GLUTmenu *));
} else {
/* XXX Some realloc's do not correctly perform a malloc
when asked to perform a realloc on a NULL pointer,
though the ANSI C library spec requires this. */
menuList = (GLUTmenu **) malloc(sizeof(GLUTmenu *));
}
if (!menuList) {
__glutFatalError("out of memory.");
}
menuList[menuListSize - 1] = NULL;
return menuListSize - 1;
}
static void
menuModificationError(void)
{
/* XXX Remove the warning after GLUT 3.0. */
__glutWarning("The following is a new check for GLUT 3.0; update your code.");
__glutFatalError("menu manipulation not allowed while menus in use.");
}
int GLUTAPIENTRY
glutCreateMenu(GLUTselectCB selectFunc)
{
GLUTmenu *menu;
int menuid;
if (__glutMappedMenu) {
menuModificationError();
}
menuid = getUnusedMenuSlot();
menu = (GLUTmenu *) malloc(sizeof(GLUTmenu));
if (!menu) {
__glutFatalError("out of memory.");
}
menu->id = menuid;
menu->num = 0;
menu->submenus = 0;
menu->select = selectFunc;
menu->list = NULL;
menu->cascade = NULL;
menu->highlighted = NULL;
menu->anchor = NULL;
menu->win = (HWND) CreatePopupMenu();
menuList[menuid] = menu;
__glutSetMenu(menu);
return menuid + 1;
}
int GLUTAPIENTRY
__glutCreateMenuWithExit(GLUTselectCB selectFunc, void (__cdecl *exitfunc)(int))
{
__glutExitFunc = exitfunc;
return glutCreateMenu(selectFunc);
}
void GLUTAPIENTRY
glutDestroyMenu(int menunum)
{
GLUTmenu *menu = __glutGetMenuByNum(menunum);
GLUTmenuItem *item, *next;
if (__glutMappedMenu) {
menuModificationError();
}
assert(menu->id == menunum - 1);
DestroyMenu( (HMENU) menu->win);
menuList[menunum - 1] = NULL;
/* free all menu entries */
item = menu->list;
while (item) {
assert(item->menu == menu);
next = item->next;
free(item->label);
free(item);
item = next;
}
if (__glutCurrentMenu == menu) {
__glutCurrentMenu = NULL;
}
free(menu);
}
int GLUTAPIENTRY
glutGetMenu(void)
{
if (__glutCurrentMenu) {
return __glutCurrentMenu->id + 1;
} else {
return 0;
}
}
void GLUTAPIENTRY
glutSetMenu(int menuid)
{
GLUTmenu *menu;
if (menuid < 1 || menuid > menuListSize) {
__glutWarning("glutSetMenu attempted on bogus menu.");
return;
}
menu = menuList[menuid - 1];
if (!menu) {
__glutWarning("glutSetMenu attempted on bogus menu.");
return;
}
__glutSetMenu(menu);
}
static void
setMenuItem(GLUTmenuItem * item, const char *label,
int value, Bool isTrigger)
{
GLUTmenu *menu;
menu = item->menu;
item->label = __glutStrdup(label);
if (!item->label) {
__glutFatalError("out of memory.");
}
item->isTrigger = isTrigger;
item->len = (int) strlen(label);
item->value = value;
item->unique = uniqueMenuHandler++;
if (isTrigger) {
AppendMenu((HMENU) menu->win, MF_POPUP, (UINT)item->win, label);
} else {
AppendMenu((HMENU) menu->win, MF_STRING, item->unique, label);
}
}
void GLUTAPIENTRY
glutAddMenuEntry(const char *label, int value)
{
GLUTmenuItem *entry;
if (__glutMappedMenu) {
menuModificationError();
}
entry = (GLUTmenuItem *) malloc(sizeof(GLUTmenuItem));
if (!entry) {
__glutFatalError("out of memory.");
}
entry->menu = __glutCurrentMenu;
setMenuItem(entry, label, value, FALSE);
__glutCurrentMenu->num++;
entry->next = __glutCurrentMenu->list;
__glutCurrentMenu->list = entry;
}
void GLUTAPIENTRY
glutAddSubMenu(const char *label, int menu)
{
GLUTmenuItem *submenu;
GLUTmenu *popupmenu;
if (__glutMappedMenu) {
menuModificationError();
}
submenu = (GLUTmenuItem *) malloc(sizeof(GLUTmenuItem));
if (!submenu) {
__glutFatalError("out of memory.");
}
__glutCurrentMenu->submenus++;
submenu->menu = __glutCurrentMenu;
popupmenu = __glutGetMenuByNum(menu);
if (popupmenu) {
submenu->win = popupmenu->win;
}
setMenuItem(submenu, label, /* base 0 */ menu - 1, TRUE);
__glutCurrentMenu->num++;
submenu->next = __glutCurrentMenu->list;
__glutCurrentMenu->list = submenu;
}
void GLUTAPIENTRY
glutChangeToMenuEntry(int num, const char *label, int value)
{
GLUTmenuItem *item;
int i;
if (__glutMappedMenu) {
menuModificationError();
}
i = __glutCurrentMenu->num;
item = __glutCurrentMenu->list;
while (item) {
if (i == num) {
if (item->isTrigger) {
/* If changing a submenu trigger to a menu entry, we
need to account for submenus. */
item->menu->submenus--;
/* Nuke the Win32 menu. */
DestroyMenu((HMENU) item->win);
}
free(item->label);
item->label = strdup(label);
if (!item->label)
__glutFatalError("out of memory");
item->isTrigger = FALSE;
item->len = (int) strlen(label);
item->value = value;
item->unique = uniqueMenuHandler++;
ModifyMenu((HMENU) __glutCurrentMenu->win, (UINT) i - 1,
MF_BYPOSITION | MFT_STRING, item->unique, label);
return;
}
i--;
item = item->next;
}
__glutWarning("Current menu has no %d item.", num);
}
void GLUTAPIENTRY
glutChangeToSubMenu(int num, const char *label, int menu)
{
GLUTmenu *popupmenu;
GLUTmenuItem *item;
int i;
if (__glutMappedMenu) {
menuModificationError();
}
i = __glutCurrentMenu->num;
item = __glutCurrentMenu->list;
while (item) {
if (i == num) {
if (!item->isTrigger) {
/* If changing a menu entry to as submenu trigger, we
need to account for submenus. */
item->menu->submenus++;
item->win = (HWND) CreatePopupMenu();
}
free(item->label);
item->label = strdup(label);
if (!item->label)
__glutFatalError("out of memory");
item->isTrigger = TRUE;
item->len = (int) strlen(label);
item->value = menu - 1;
item->unique = uniqueMenuHandler++;
popupmenu = __glutGetMenuByNum(menu);
if (popupmenu)
item->win = popupmenu->win;
ModifyMenu((HMENU) __glutCurrentMenu->win, (UINT) i - 1,
MF_BYPOSITION | MF_POPUP, (UINT) item->win, label);
return;
}
i--;
item = item->next;
}
__glutWarning("Current menu has no %d item.", num);
}
void GLUTAPIENTRY
glutRemoveMenuItem(int num)
{
GLUTmenuItem *item, **prev;
int i;
if (__glutMappedMenu) {
menuModificationError();
}
i = __glutCurrentMenu->num;
prev = &__glutCurrentMenu->list;
item = __glutCurrentMenu->list;
while (item) {
if (i == num) {
/* Found the menu item in list to remove. */
__glutCurrentMenu->num--;
/* Patch up menu's item list. */
*prev = item->next;
RemoveMenu((HMENU) __glutCurrentMenu->win, (UINT) i - 1, MF_BYPOSITION);
free(item->label);
free(item);
return;
}
i--;
prev = &item->next;
item = item->next;
}
__glutWarning("Current menu has no %d item.", num);
}
void GLUTAPIENTRY
glutAttachMenu(int button)
{
if (__glutCurrentWindow == __glutGameModeWindow) {
__glutWarning("cannot attach menus in game mode.");
return;
}
if (__glutMappedMenu) {
menuModificationError();
}
if (__glutCurrentWindow->menu[button] < 1) {
__glutCurrentWindow->buttonUses++;
}
__glutCurrentWindow->menu[button] = __glutCurrentMenu->id + 1;
}
void GLUTAPIENTRY
glutDetachMenu(int button)
{
if (__glutMappedMenu) {
menuModificationError();
}
if (__glutCurrentWindow->menu[button] > 0) {
__glutCurrentWindow->buttonUses--;
__glutCurrentWindow->menu[button] = 0;
}
}