Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/freeglut/src/fg_menu.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /* | |
| 2 * fg_menu.c | |
| 3 * | |
| 4 * Pull-down menu creation and handling. | |
| 5 * | |
| 6 * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved. | |
| 7 * Written by Pawel W. Olszta, <olszta@sourceforge.net> | |
| 8 * Creation date: Thu Dec 16 1999 | |
| 9 * | |
| 10 * Permission is hereby granted, free of charge, to any person obtaining a | |
| 11 * copy of this software and associated documentation files (the "Software"), | |
| 12 * to deal in the Software without restriction, including without limitation | |
| 13 * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 14 * and/or sell copies of the Software, and to permit persons to whom the | |
| 15 * Software is furnished to do so, subject to the following conditions: | |
| 16 * | |
| 17 * The above copyright notice and this permission notice shall be included | |
| 18 * in all copies or substantial portions of the Software. | |
| 19 * | |
| 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
| 21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 23 * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
| 24 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
| 25 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| 26 */ | |
| 27 | |
| 28 #define FREEGLUT_BUILDING_LIB | |
| 29 #include <GL/freeglut.h> | |
| 30 #include "fg_internal.h" | |
| 31 | |
| 32 | |
| 33 /* -- DEFINITIONS ---------------------------------------------------------- */ | |
| 34 | |
| 35 /* | |
| 36 * FREEGLUT_MENU_FONT can be any freeglut bitmapped font. | |
| 37 * (Stroked fonts would not be out of the question, but we'd need to alter | |
| 38 * code, since GLUT (hence freeglut) does not quite unify stroked and | |
| 39 * bitmapped font handling.) | |
| 40 * Old UNIX/X11 GLUT (BSD, UNIX, IRIX, LINUX, HPUX, ...) used a system | |
| 41 * font best approximated by an 18-pixel HELVETICA, I think. MS-WINDOWS | |
| 42 * GLUT used something closest to the 8x13 fixed-width font. (Old | |
| 43 * GLUT apparently uses host-system menus rather than building its own. | |
| 44 * freeglut is building its own menus from scratch.) | |
| 45 * | |
| 46 * FREEGLUT_MENUENTRY_HEIGHT gives the height of ONE menu box. This should | |
| 47 * be the distances between two adjacent menu entries. It should scale | |
| 48 * automatically with the font choice, so you needn't alter it---unless you | |
| 49 * use a stroked font. | |
| 50 * | |
| 51 * FREEGLUT_MENU_BORDER says how many pixels to allow around the edge of a | |
| 52 * menu. (It also seems to be the same as the number of pixels used as | |
| 53 * a border around *items* to separate them from neighbors. John says | |
| 54 * that that wasn't the original intent...if not, perhaps we need another | |
| 55 * symbolic constant, FREEGLUT_MENU_ITEM_BORDER, or such.) | |
| 56 */ | |
| 57 /* See platform-specific header files for menu font and color definitions */ | |
| 58 | |
| 59 #define FREEGLUT_MENUENTRY_HEIGHT(font) (glutBitmapHeight(font) + \ | |
| 60 FREEGLUT_MENU_BORDER) | |
| 61 #define FREEGLUT_MENU_BORDER 2 | |
| 62 | |
| 63 | |
| 64 /* | |
| 65 * These variables are for rendering the freeglut menu items. | |
| 66 * | |
| 67 * The choices are fore- and background, with and without h for Highlighting. | |
| 68 * Old GLUT appeared to be system-dependent for its colors (sigh) so we are | |
| 69 * too. These variables should be stuffed into global state and initialized | |
| 70 * via the glutInit*() system. | |
| 71 */ | |
| 72 static float menu_pen_fore [4] = FREEGLUT_MENU_PEN_FORE_COLORS ; | |
| 73 static float menu_pen_back [4] = FREEGLUT_MENU_PEN_BACK_COLORS ; | |
| 74 static float menu_pen_hfore [4] = FREEGLUT_MENU_PEN_HFORE_COLORS; | |
| 75 static float menu_pen_hback [4] = FREEGLUT_MENU_PEN_HBACK_COLORS; | |
| 76 | |
| 77 | |
| 78 extern GLvoid fgPlatformGetGameModeVMaxExtent( SFG_Window* window, int* x, int* y ); | |
| 79 extern void fghPlatformGetCursorPos(const SFG_Window *window, GLboolean client, SFG_XYUse *mouse_pos); | |
| 80 extern SFG_Font* fghFontByID( void* font ); | |
| 81 extern void fgPlatformHideWindow( SFG_Window* window ); | |
| 82 | |
| 83 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */ | |
| 84 | |
| 85 /* | |
| 86 * Private function to find a menu entry by index | |
| 87 */ | |
| 88 static SFG_MenuEntry *fghFindMenuEntry( SFG_Menu* menu, int index ) | |
| 89 { | |
| 90 SFG_MenuEntry *entry; | |
| 91 int i = 1; | |
| 92 | |
| 93 for( entry = (SFG_MenuEntry *)menu->Entries.First; | |
| 94 entry; | |
| 95 entry = (SFG_MenuEntry *)entry->Node.Next ) | |
| 96 { | |
| 97 if( i == index ) | |
| 98 break; | |
| 99 ++i; | |
| 100 } | |
| 101 | |
| 102 return entry; | |
| 103 } | |
| 104 | |
| 105 /* | |
| 106 * Deactivates a menu pointed by the function argument. | |
| 107 */ | |
| 108 static void fghDeactivateSubMenu( SFG_MenuEntry *menuEntry ) | |
| 109 { | |
| 110 SFG_MenuEntry *subMenuIter; | |
| 111 /* Hide the present menu's window */ | |
| 112 fgPlatformHideWindow( menuEntry->SubMenu->Window ); | |
| 113 | |
| 114 /* Forget about having that menu active anymore, now: */ | |
| 115 menuEntry->SubMenu->Window->ActiveMenu = NULL; | |
| 116 menuEntry->SubMenu->IsActive = GL_FALSE; | |
| 117 menuEntry->SubMenu->ActiveEntry = NULL; | |
| 118 | |
| 119 /* Hide all submenu windows, and the root menu's window. */ | |
| 120 for ( subMenuIter = (SFG_MenuEntry *)menuEntry->SubMenu->Entries.First; | |
| 121 subMenuIter; | |
| 122 subMenuIter = (SFG_MenuEntry *)subMenuIter->Node.Next ) | |
| 123 { | |
| 124 subMenuIter->IsActive = GL_FALSE; | |
| 125 | |
| 126 /* Is that an active submenu by any case? */ | |
| 127 if( subMenuIter->SubMenu ) | |
| 128 fghDeactivateSubMenu( subMenuIter ); | |
| 129 } | |
| 130 } | |
| 131 | |
| 132 /* | |
| 133 * Private function to get the virtual maximum screen extent | |
| 134 */ | |
| 135 static GLvoid fghGetVMaxExtent( SFG_Window* window, int* x, int* y ) | |
| 136 { | |
| 137 if( fgStructure.GameModeWindow ) | |
| 138 fgPlatformGetGameModeVMaxExtent ( window, x, y ); | |
| 139 else | |
| 140 { | |
| 141 *x = fgDisplay.ScreenWidth; | |
| 142 *y = fgDisplay.ScreenHeight; | |
| 143 } | |
| 144 } | |
| 145 | |
| 146 /* | |
| 147 * Private function to check for the current menu/sub menu activity state | |
| 148 */ | |
| 149 static GLboolean fghCheckMenuStatus( SFG_Menu* menu ) | |
| 150 { | |
| 151 SFG_MenuEntry* menuEntry; | |
| 152 int x, y; | |
| 153 | |
| 154 /* First of all check any of the active sub menus... */ | |
| 155 for( menuEntry = (SFG_MenuEntry *)menu->Entries.First; | |
| 156 menuEntry; | |
| 157 menuEntry = (SFG_MenuEntry *)menuEntry->Node.Next ) | |
| 158 { | |
| 159 if( menuEntry->SubMenu && menuEntry->IsActive ) | |
| 160 { | |
| 161 /* | |
| 162 * OK, have the sub-menu checked, too. If it returns GL_TRUE, it | |
| 163 * will mean that it caught the mouse cursor and we do not need | |
| 164 * to regenerate the activity list, and so our parents do... | |
| 165 */ | |
| 166 GLboolean return_status; | |
| 167 | |
| 168 menuEntry->SubMenu->Window->State.MouseX = | |
| 169 menu->Window->State.MouseX + menu->X - menuEntry->SubMenu->X; | |
| 170 menuEntry->SubMenu->Window->State.MouseY = | |
| 171 menu->Window->State.MouseY + menu->Y - menuEntry->SubMenu->Y; | |
| 172 return_status = fghCheckMenuStatus( menuEntry->SubMenu ); | |
| 173 | |
| 174 if ( return_status ) | |
| 175 return GL_TRUE; | |
| 176 } | |
| 177 } | |
| 178 | |
| 179 /* That much about our sub menus, let's get to checking the current menu: */ | |
| 180 x = menu->Window->State.MouseX; | |
| 181 y = menu->Window->State.MouseY; | |
| 182 | |
| 183 /* Check if the mouse cursor is contained within the current menu box */ | |
| 184 if( ( x >= FREEGLUT_MENU_BORDER ) && | |
| 185 ( x < menu->Width - FREEGLUT_MENU_BORDER ) && | |
| 186 ( y >= FREEGLUT_MENU_BORDER ) && | |
| 187 ( y < menu->Height - FREEGLUT_MENU_BORDER ) ) | |
| 188 { | |
| 189 int menuID = ( y - FREEGLUT_MENU_BORDER ) / FREEGLUT_MENUENTRY_HEIGHT(menu->Font); | |
| 190 | |
| 191 /* The mouse cursor is somewhere over our box, check it out. */ | |
| 192 menuEntry = fghFindMenuEntry( menu, menuID + 1 ); | |
| 193 FREEGLUT_INTERNAL_ERROR_EXIT( menuEntry, "Cannot find menu entry", | |
| 194 "fghCheckMenuStatus" ); | |
| 195 | |
| 196 menuEntry->IsActive = GL_TRUE; | |
| 197 menuEntry->Ordinal = menuID; | |
| 198 | |
| 199 /* | |
| 200 * If this is not the same as the last active menu entry, deactivate | |
| 201 * the previous entry. Specifically, if the previous active entry | |
| 202 * was a submenu then deactivate it. | |
| 203 */ | |
| 204 if( menu->ActiveEntry && ( menuEntry != menu->ActiveEntry ) ) | |
| 205 if( menu->ActiveEntry->SubMenu ) | |
| 206 fghDeactivateSubMenu( menu->ActiveEntry ); | |
| 207 | |
| 208 if( menuEntry != menu->ActiveEntry ) | |
| 209 { | |
| 210 menu->Window->State.WorkMask |= GLUT_DISPLAY_WORK; | |
| 211 if( menu->ActiveEntry ) | |
| 212 menu->ActiveEntry->IsActive = GL_FALSE; | |
| 213 } | |
| 214 | |
| 215 menu->ActiveEntry = menuEntry; | |
| 216 menu->IsActive = GL_TRUE; | |
| 217 | |
| 218 /* | |
| 219 * OK, we have marked that entry as active, but it would be also | |
| 220 * nice to have its contents updated, in case it's a sub menu. | |
| 221 * Also, ignore the return value of the check function: | |
| 222 */ | |
| 223 if( menuEntry->SubMenu ) | |
| 224 { | |
| 225 if ( ! menuEntry->SubMenu->IsActive ) | |
| 226 { | |
| 227 int max_x, max_y; | |
| 228 SFG_Window *current_window = fgStructure.CurrentWindow; | |
| 229 | |
| 230 /* Set up the initial menu position now... */ | |
| 231 menuEntry->SubMenu->IsActive = GL_TRUE; | |
| 232 | |
| 233 /* Set up the initial submenu position now: */ | |
| 234 fghGetVMaxExtent(menu->ParentWindow, &max_x, &max_y); | |
| 235 menuEntry->SubMenu->X = menu->X + menu->Width; | |
| 236 menuEntry->SubMenu->Y = menu->Y + | |
| 237 menuEntry->Ordinal * FREEGLUT_MENUENTRY_HEIGHT(menu->Font); | |
| 238 | |
| 239 if( menuEntry->SubMenu->X + menuEntry->SubMenu->Width > max_x ) | |
| 240 menuEntry->SubMenu->X = menu->X - menuEntry->SubMenu->Width; | |
| 241 | |
| 242 if( menuEntry->SubMenu->Y + menuEntry->SubMenu->Height > max_y ) | |
| 243 { | |
| 244 menuEntry->SubMenu->Y -= ( menuEntry->SubMenu->Height - | |
| 245 FREEGLUT_MENUENTRY_HEIGHT(menu->Font) - | |
| 246 2 * FREEGLUT_MENU_BORDER ); | |
| 247 if( menuEntry->SubMenu->Y < 0 ) | |
| 248 menuEntry->SubMenu->Y = 0; | |
| 249 } | |
| 250 | |
| 251 fgSetWindow( menuEntry->SubMenu->Window ); | |
| 252 glutPositionWindow( menuEntry->SubMenu->X, | |
| 253 menuEntry->SubMenu->Y ); | |
| 254 glutReshapeWindow( menuEntry->SubMenu->Width, | |
| 255 menuEntry->SubMenu->Height ); | |
| 256 glutPopWindow( ); | |
| 257 glutShowWindow( ); | |
| 258 menuEntry->SubMenu->Window->ActiveMenu = menuEntry->SubMenu; | |
| 259 fgSetWindow( current_window ); | |
| 260 menuEntry->SubMenu->Window->State.MouseX = | |
| 261 x + menu->X - menuEntry->SubMenu->X; | |
| 262 menuEntry->SubMenu->Window->State.MouseY = | |
| 263 y + menu->Y - menuEntry->SubMenu->Y; | |
| 264 fghCheckMenuStatus( menuEntry->SubMenu ); | |
| 265 } | |
| 266 | |
| 267 /* Activate it because its parent entry is active */ | |
| 268 menuEntry->SubMenu->IsActive = GL_TRUE; | |
| 269 } | |
| 270 | |
| 271 /* Report back that we have caught the menu cursor */ | |
| 272 return GL_TRUE; | |
| 273 } | |
| 274 | |
| 275 /* Looks like the menu cursor is somewhere else... */ | |
| 276 if( menu->ActiveEntry && menu->ActiveEntry->IsActive && | |
| 277 ( !menu->ActiveEntry->SubMenu || | |
| 278 !menu->ActiveEntry->SubMenu->IsActive ) ) | |
| 279 { | |
| 280 menu->Window->State.WorkMask |= GLUT_DISPLAY_WORK; | |
| 281 menu->ActiveEntry->IsActive = GL_FALSE; | |
| 282 menu->ActiveEntry = NULL; | |
| 283 } | |
| 284 | |
| 285 return GL_FALSE; | |
| 286 } | |
| 287 | |
| 288 /* | |
| 289 * Displays a menu box and all of its submenus (if they are active) | |
| 290 */ | |
| 291 static void fghDisplayMenuBox( SFG_Menu* menu ) | |
| 292 { | |
| 293 SFG_MenuEntry *menuEntry; | |
| 294 int i; | |
| 295 int border = FREEGLUT_MENU_BORDER; | |
| 296 | |
| 297 /* | |
| 298 * Have the menu box drawn first. The +- values are | |
| 299 * here just to make it more nice-looking... | |
| 300 */ | |
| 301 /* a non-black dark version of the below. */ | |
| 302 glColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); | |
| 303 glBegin( GL_QUAD_STRIP ); | |
| 304 glVertex2i( menu->Width , 0 ); | |
| 305 glVertex2i( menu->Width - border, border); | |
| 306 glVertex2i( 0 , 0 ); | |
| 307 glVertex2i( border, border); | |
| 308 glVertex2i( 0 , menu->Height ); | |
| 309 glVertex2i( border, menu->Height - border); | |
| 310 glEnd( ); | |
| 311 | |
| 312 /* a non-black dark version of the below. */ | |
| 313 glColor4f( 0.5f, 0.5f, 0.5f, 1.0f ); | |
| 314 glBegin( GL_QUAD_STRIP ); | |
| 315 glVertex2i( 0 , menu->Height ); | |
| 316 glVertex2i( border, menu->Height - border); | |
| 317 glVertex2i( menu->Width , menu->Height ); | |
| 318 glVertex2i( menu->Width - border, menu->Height - border); | |
| 319 glVertex2i( menu->Width , 0 ); | |
| 320 glVertex2i( menu->Width - border, border); | |
| 321 glEnd( ); | |
| 322 | |
| 323 glColor4fv( menu_pen_back ); | |
| 324 glBegin( GL_QUADS ); | |
| 325 glVertex2i( border, border); | |
| 326 glVertex2i( menu->Width - border, border); | |
| 327 glVertex2i( menu->Width - border, menu->Height - border); | |
| 328 glVertex2i( border, menu->Height - border); | |
| 329 glEnd( ); | |
| 330 | |
| 331 /* Check if any of the submenus is currently active... */ | |
| 332 for( menuEntry = (SFG_MenuEntry *)menu->Entries.First; | |
| 333 menuEntry; | |
| 334 menuEntry = (SFG_MenuEntry *)menuEntry->Node.Next ) | |
| 335 { | |
| 336 /* Has the menu been marked as active, maybe? */ | |
| 337 if( menuEntry->IsActive ) | |
| 338 { | |
| 339 /* | |
| 340 * That's truly right, and we need to have it highlighted. | |
| 341 * There is an assumption that mouse cursor didn't move | |
| 342 * since the last check of menu activity state: | |
| 343 */ | |
| 344 int menuID = menuEntry->Ordinal; | |
| 345 | |
| 346 /* So have the highlight drawn... */ | |
| 347 glColor4fv( menu_pen_hback ); | |
| 348 glBegin( GL_QUADS ); | |
| 349 glVertex2i( border, | |
| 350 (menuID + 0)*FREEGLUT_MENUENTRY_HEIGHT(menu->Font) + border ); | |
| 351 glVertex2i( menu->Width - border, | |
| 352 (menuID + 0)*FREEGLUT_MENUENTRY_HEIGHT(menu->Font) + border ); | |
| 353 glVertex2i( menu->Width - border, | |
| 354 (menuID + 1)*FREEGLUT_MENUENTRY_HEIGHT(menu->Font) + border ); | |
| 355 glVertex2i( border, | |
| 356 (menuID + 1)*FREEGLUT_MENUENTRY_HEIGHT(menu->Font) + border ); | |
| 357 glEnd( ); | |
| 358 } | |
| 359 } | |
| 360 | |
| 361 /* Print the menu entries now... */ | |
| 362 | |
| 363 glColor4fv( menu_pen_fore ); | |
| 364 | |
| 365 for( menuEntry = (SFG_MenuEntry *)menu->Entries.First, i = 0; | |
| 366 menuEntry; | |
| 367 menuEntry = (SFG_MenuEntry *)menuEntry->Node.Next, ++i ) | |
| 368 { | |
| 369 /* If the menu entry is active, set the color to white */ | |
| 370 if( menuEntry->IsActive ) | |
| 371 glColor4fv( menu_pen_hfore ); | |
| 372 | |
| 373 /* Move the raster into position... */ | |
| 374 /* Try to center the text - JCJ 31 July 2003*/ | |
| 375 glRasterPos2i( | |
| 376 2 * border, | |
| 377 ( i + 1 )*FREEGLUT_MENUENTRY_HEIGHT(menu->Font) - | |
| 378 ( int )( FREEGLUT_MENUENTRY_HEIGHT(menu->Font)*0.3 - border ) | |
| 379 ); | |
| 380 | |
| 381 /* Have the label drawn, character after character: */ | |
| 382 glutBitmapString( menu->Font, | |
| 383 (unsigned char *)menuEntry->Text); | |
| 384 | |
| 385 /* If it's a submenu, draw a right arrow */ | |
| 386 if( menuEntry->SubMenu ) | |
| 387 { | |
| 388 int width = glutBitmapWidth( menu->Font, '_' ); | |
| 389 int x_base = menu->Width - 2 - width; | |
| 390 int y_base = i*FREEGLUT_MENUENTRY_HEIGHT(menu->Font) + border; | |
| 391 glBegin( GL_TRIANGLES ); | |
| 392 glVertex2i( x_base, y_base + 2*border); | |
| 393 glVertex2i( menu->Width - 2, y_base + | |
| 394 ( FREEGLUT_MENUENTRY_HEIGHT(menu->Font) + border) / 2 ); | |
| 395 glVertex2i( x_base, y_base + FREEGLUT_MENUENTRY_HEIGHT(menu->Font) - border ); | |
| 396 glEnd( ); | |
| 397 } | |
| 398 | |
| 399 /* If the menu entry is active, reset the color */ | |
| 400 if( menuEntry->IsActive ) | |
| 401 glColor4fv( menu_pen_fore ); | |
| 402 } | |
| 403 } | |
| 404 | |
| 405 /* | |
| 406 * Private static function to set the parent window of a submenu and all | |
| 407 * of its submenus. | |
| 408 */ | |
| 409 static void fghSetMenuParentWindow( SFG_Window *window, SFG_Menu *menu ) | |
| 410 { | |
| 411 SFG_MenuEntry *menuEntry; | |
| 412 | |
| 413 menu->ParentWindow = window; | |
| 414 | |
| 415 for( menuEntry = ( SFG_MenuEntry * )menu->Entries.First; | |
| 416 menuEntry; | |
| 417 menuEntry = ( SFG_MenuEntry * )menuEntry->Node.Next ) | |
| 418 if( menuEntry->SubMenu ) | |
| 419 fghSetMenuParentWindow( window, menuEntry->SubMenu ); | |
| 420 } | |
| 421 | |
| 422 | |
| 423 /* | |
| 424 * Displays the currently active menu for the current window | |
| 425 */ | |
| 426 void fgDisplayMenu( void ) | |
| 427 { | |
| 428 SFG_Window* window = fgStructure.CurrentWindow; | |
| 429 SFG_Menu* menu = NULL; | |
| 430 | |
| 431 FREEGLUT_INTERNAL_ERROR_EXIT ( fgStructure.CurrentWindow, "Displaying menu in nonexistent window", | |
| 432 "fgDisplayMenu" ); | |
| 433 | |
| 434 /* Check if there is an active menu attached to this window... */ | |
| 435 menu = window->ActiveMenu; | |
| 436 freeglut_return_if_fail( menu ); | |
| 437 | |
| 438 fgSetWindow( menu->Window ); | |
| 439 | |
| 440 glPushAttrib( GL_DEPTH_BUFFER_BIT | GL_TEXTURE_BIT | GL_LIGHTING_BIT | | |
| 441 GL_POLYGON_BIT ); | |
| 442 | |
| 443 glDisable( GL_DEPTH_TEST ); | |
| 444 glDisable( GL_TEXTURE_2D ); | |
| 445 glDisable( GL_LIGHTING ); | |
| 446 glDisable( GL_CULL_FACE ); | |
| 447 | |
| 448 glMatrixMode( GL_PROJECTION ); | |
| 449 glPushMatrix( ); | |
| 450 glLoadIdentity( ); | |
| 451 glOrtho( | |
| 452 0, glutGet( GLUT_WINDOW_WIDTH ), | |
| 453 glutGet( GLUT_WINDOW_HEIGHT ), 0, | |
| 454 -1, 1 | |
| 455 ); | |
| 456 | |
| 457 glMatrixMode( GL_MODELVIEW ); | |
| 458 glPushMatrix( ); | |
| 459 glLoadIdentity( ); | |
| 460 | |
| 461 fghDisplayMenuBox( menu ); | |
| 462 | |
| 463 glPopAttrib( ); | |
| 464 | |
| 465 glMatrixMode( GL_PROJECTION ); | |
| 466 glPopMatrix( ); | |
| 467 glMatrixMode( GL_MODELVIEW ); | |
| 468 glPopMatrix( ); | |
| 469 | |
| 470 glutSwapBuffers( ); | |
| 471 | |
| 472 fgSetWindow ( window ); | |
| 473 } | |
| 474 | |
| 475 /* | |
| 476 * Activates a menu pointed by the function argument | |
| 477 */ | |
| 478 static void fghActivateMenu( SFG_Window* window, int button ) | |
| 479 { | |
| 480 int max_x, max_y; | |
| 481 SFG_XYUse mouse_pos; | |
| 482 | |
| 483 /* We'll be referencing this menu a lot, so remember its address: */ | |
| 484 SFG_Menu* menu = window->Menu[ button ]; | |
| 485 SFG_Window* current_window = fgStructure.CurrentWindow; | |
| 486 | |
| 487 /* If the menu is already active in another window, deactivate it (and any submenus) there */ | |
| 488 if ( menu->ParentWindow ) | |
| 489 fgDeactivateMenu(menu->ParentWindow); | |
| 490 | |
| 491 /* Mark the menu as active, so that it gets displayed: */ | |
| 492 window->ActiveMenu = menu; | |
| 493 menu->IsActive = GL_TRUE; | |
| 494 fghSetMenuParentWindow ( window, menu ); | |
| 495 fgState.ActiveMenus++; | |
| 496 | |
| 497 /* Set up the initial menu position now: */ | |
| 498 fghGetVMaxExtent(menu->ParentWindow, &max_x, &max_y); | |
| 499 fgSetWindow( window ); | |
| 500 /* get mouse position on screen (window->State.MouseX and window->State.MouseY | |
| 501 * are relative to client area origin), and not easy to correct given that | |
| 502 * glutGet( GLUT_WINDOW_X ) and glutGet( GLUT_WINDOW_Y ) return relative to parent | |
| 503 * origin when looking at a child window | |
| 504 * for parent windows: window->State.MouseX + glutGet( GLUT_WINDOW_X ) == mouse_pos.X | |
| 505 */ | |
| 506 fghPlatformGetCursorPos(NULL, GL_FALSE, &mouse_pos); | |
| 507 menu->X = mouse_pos.X; | |
| 508 menu->Y = mouse_pos.Y; | |
| 509 | |
| 510 /* Make sure the whole menu is on the screen */ | |
| 511 if( menu->X + menu->Width > max_x ) | |
| 512 menu->X -=menu->Width; | |
| 513 | |
| 514 if( menu->Y + menu->Height > max_y ) | |
| 515 { | |
| 516 menu->Y -=menu->Height; | |
| 517 if( menu->Y < 0 ) | |
| 518 menu->Y = 0; | |
| 519 } | |
| 520 | |
| 521 /* Set position of mouse relative to top-left menu in menu's window state (could as well set 0 at creation time...) */ | |
| 522 menu->Window->State.MouseX = mouse_pos.X - menu->X; | |
| 523 menu->Window->State.MouseY = mouse_pos.Y - menu->Y; | |
| 524 | |
| 525 /* Menu status callback */ | |
| 526 if (fgState.MenuStateCallback || fgState.MenuStatusCallback) | |
| 527 { | |
| 528 fgStructure.CurrentMenu = menu; | |
| 529 fgStructure.CurrentWindow = window; | |
| 530 if (fgState.MenuStateCallback) | |
| 531 fgState.MenuStateCallback(GLUT_MENU_IN_USE); | |
| 532 if (fgState.MenuStatusCallback) | |
| 533 /* window->State.MouseX and window->State.MouseY are relative to client area origin, as needed */ | |
| 534 fgState.MenuStatusCallback(GLUT_MENU_IN_USE, window->State.MouseX, window->State.MouseY); | |
| 535 } | |
| 536 | |
| 537 fgSetWindow( menu->Window ); | |
| 538 glutPositionWindow( menu->X, menu->Y ); | |
| 539 glutReshapeWindow( menu->Width, menu->Height ); | |
| 540 glutPopWindow( ); | |
| 541 glutShowWindow( ); | |
| 542 menu->Window->ActiveMenu = menu; | |
| 543 fghCheckMenuStatus( menu ); | |
| 544 fgSetWindow( current_window ); | |
| 545 } | |
| 546 | |
| 547 /* | |
| 548 * Update Highlight states of the menu | |
| 549 * NB: Current mouse position is in menu->Window->State.MouseX/Y | |
| 550 */ | |
| 551 void fgUpdateMenuHighlight ( SFG_Menu *menu ) | |
| 552 { | |
| 553 fghCheckMenuStatus( menu ); | |
| 554 } | |
| 555 | |
| 556 /* | |
| 557 * Check whether an active menu absorbs a mouse click | |
| 558 */ | |
| 559 GLboolean fgCheckActiveMenu ( SFG_Window *window, int button, GLboolean pressed, | |
| 560 int mouse_x, int mouse_y ) | |
| 561 { | |
| 562 GLboolean is_handled = GL_FALSE; | |
| 563 GLboolean is_clicked = GL_FALSE; | |
| 564 /* | |
| 565 * Near as I can tell, this is the menu behaviour: | |
| 566 * - Down-click the menu button, menu not active: activate | |
| 567 * the menu with its upper left-hand corner at the mouse | |
| 568 * location. | |
| 569 * - Down-click any button outside the menu, menu active: | |
| 570 * deactivate the menu, and potentially activate a new menu | |
| 571 * at the new mouse location. This includes clicks in | |
| 572 * different windows of course | |
| 573 * - Down-click any button inside the menu, menu active: | |
| 574 * select the menu entry and deactivate the menu | |
| 575 * - Up-click the menu button, menu not active: nothing happens | |
| 576 * - Up-click the menu button outside the menu, menu active: | |
| 577 * nothing happens | |
| 578 * - Up-click the menu button inside the menu, menu active: | |
| 579 * select the menu entry and deactivate the menu | |
| 580 * Since menus can have submenus, we need to check this recursively. | |
| 581 */ | |
| 582 if( window->ActiveMenu ) | |
| 583 { | |
| 584 if( window == window->ActiveMenu->ParentWindow ) | |
| 585 { | |
| 586 window->ActiveMenu->Window->State.MouseX = | |
| 587 mouse_x - window->ActiveMenu->X; | |
| 588 window->ActiveMenu->Window->State.MouseY = | |
| 589 mouse_y - window->ActiveMenu->Y; | |
| 590 } | |
| 591 | |
| 592 /* In the menu, deactivate the menu and invoke the callback */ | |
| 593 if( fghCheckMenuStatus( window->ActiveMenu ) ) | |
| 594 { | |
| 595 /* | |
| 596 * Save the current window and menu and set the current | |
| 597 * window to the window whose menu this is | |
| 598 */ | |
| 599 SFG_Window *save_window = fgStructure.CurrentWindow; | |
| 600 SFG_Menu *save_menu = fgStructure.CurrentMenu, *active_menu = window->ActiveMenu; /* active menu is always the one with the mouse in it, due to fghCheckMenuStatus */ | |
| 601 SFG_MenuEntry *active_entry = active_menu->ActiveEntry; /* currently highlighted item -> must be the one that was just clicked */ | |
| 602 SFG_Window *parent_window = window->ActiveMenu->ParentWindow; | |
| 603 | |
| 604 /* ignore clicks on the submenu entry */ | |
| 605 if (!active_entry->SubMenu) | |
| 606 { | |
| 607 fgSetWindow( parent_window ); | |
| 608 fgStructure.CurrentMenu = active_menu; | |
| 609 | |
| 610 /* Deactivate menu and then call callback (we don't want menu to stay in view while callback is executing, and user should be able to change menus in callback) */ | |
| 611 fgDeactivateMenu( parent_window ); | |
| 612 active_menu->Callback( active_entry->ID ); | |
| 613 | |
| 614 /* Restore the current window and menu */ | |
| 615 fgSetWindow( save_window ); | |
| 616 fgStructure.CurrentMenu = save_menu; | |
| 617 } | |
| 618 | |
| 619 is_clicked = GL_TRUE; /* Don't reopen... */ | |
| 620 } | |
| 621 else if( pressed ) | |
| 622 /* | |
| 623 * Outside the menu, deactivate if it's a downclick | |
| 624 * | |
| 625 * A downclick outside of the interior of our freeglut windows | |
| 626 * is dealt with in the WM_KILLFOCUS handler of fgPlatformWindowProc | |
| 627 */ | |
| 628 { | |
| 629 fgDeactivateMenu( window->ActiveMenu->ParentWindow ); | |
| 630 /* Could reopen again in different location, as is_clicked remains false */ | |
| 631 } | |
| 632 | |
| 633 is_handled = GL_TRUE; | |
| 634 } | |
| 635 else if ( fgState.ActiveMenus ) /* Don't have to check whether this was a downpress or an uppress, there is no way to get an uppress in another window before a downpress... */ | |
| 636 { | |
| 637 /* if another window than the one clicked in has an open menu, close it */ | |
| 638 SFG_Menu *menu = fgGetActiveMenu(); | |
| 639 if ( menu ) /* any open menu? */ | |
| 640 fgDeactivateMenu( menu->ParentWindow ); | |
| 641 | |
| 642 /* Leave is_handled to false, we didn't do anything relevant from the perspective of the window that was clicked */ | |
| 643 } | |
| 644 | |
| 645 /* No active menu, let's check whether we need to activate one. */ | |
| 646 if( !is_clicked && | |
| 647 ( 0 <= button ) && | |
| 648 ( FREEGLUT_MAX_MENUS > button ) && | |
| 649 ( window->Menu[ button ] ) && | |
| 650 pressed ) | |
| 651 { | |
| 652 /* If mouseclick was outside the parent window, ignore. This can | |
| 653 * happen when another mouse button is already depressed and the | |
| 654 * window thus has mouse capture | |
| 655 */ | |
| 656 if (window->State.MouseX>0 && window->State.MouseY>0 && | |
| 657 window->State.MouseX<window->State.Width && window->State.MouseY<window->State.Height) | |
| 658 { | |
| 659 fghActivateMenu( window, button ); | |
| 660 is_handled = GL_TRUE; | |
| 661 } | |
| 662 } | |
| 663 | |
| 664 return is_handled; | |
| 665 } | |
| 666 | |
| 667 /* | |
| 668 * Deactivates a menu pointed by the function argument. | |
| 669 */ | |
| 670 static SFG_Menu* menuDeactivating = NULL; | |
| 671 void fgDeactivateMenu( SFG_Window *window ) | |
| 672 { | |
| 673 SFG_Window *parent_window = NULL; | |
| 674 SFG_Menu* menu; | |
| 675 SFG_MenuEntry *menuEntry; | |
| 676 | |
| 677 /* Did we find an active window? */ | |
| 678 freeglut_return_if_fail( window ); | |
| 679 /* Check if there is an active menu attached to this window... */ | |
| 680 menu = window->ActiveMenu; | |
| 681 freeglut_return_if_fail( menu ); | |
| 682 /* Check if we are already deactivating this menu, abort in that case (glutHideWindow below can cause this function to be called again on the same menu..) */ | |
| 683 if (menu==menuDeactivating) | |
| 684 return; | |
| 685 menuDeactivating = menu; | |
| 686 | |
| 687 parent_window = menu->ParentWindow; | |
| 688 | |
| 689 /* Hide the present menu's window */ | |
| 690 fgPlatformHideWindow( menu->Window ); | |
| 691 | |
| 692 /* Forget about having that menu active anymore, now: */ | |
| 693 menu->Window->ActiveMenu = NULL; | |
| 694 menu->ParentWindow->ActiveMenu = NULL; | |
| 695 fghSetMenuParentWindow ( NULL, menu ); | |
| 696 menu->IsActive = GL_FALSE; | |
| 697 menu->ActiveEntry = NULL; | |
| 698 | |
| 699 fgState.ActiveMenus--; | |
| 700 | |
| 701 /* Hide all submenu windows, and the root menu's window. */ | |
| 702 for ( menuEntry = ( SFG_MenuEntry * )menu->Entries.First; | |
| 703 menuEntry; | |
| 704 menuEntry = ( SFG_MenuEntry * )menuEntry->Node.Next ) | |
| 705 { | |
| 706 menuEntry->IsActive = GL_FALSE; | |
| 707 | |
| 708 /* Is that an active submenu by any chance? */ | |
| 709 if( menuEntry->SubMenu ) | |
| 710 fghDeactivateSubMenu( menuEntry ); | |
| 711 } | |
| 712 /* Done deactivating menu */ | |
| 713 menuDeactivating = NULL; | |
| 714 | |
| 715 /* Menu status callback */ | |
| 716 if (fgState.MenuStateCallback || fgState.MenuStatusCallback) | |
| 717 { | |
| 718 fgStructure.CurrentMenu = menu; | |
| 719 fgStructure.CurrentWindow = parent_window; | |
| 720 if (fgState.MenuStateCallback) | |
| 721 fgState.MenuStateCallback(GLUT_MENU_NOT_IN_USE); | |
| 722 if (fgState.MenuStatusCallback) | |
| 723 { | |
| 724 /* Get cursor position relative to parent_window's client area */ | |
| 725 SFG_XYUse mouse_pos; | |
| 726 fghPlatformGetCursorPos(parent_window, GL_TRUE, &mouse_pos); | |
| 727 | |
| 728 fgState.MenuStatusCallback(GLUT_MENU_NOT_IN_USE, mouse_pos.X, mouse_pos.Y); | |
| 729 } | |
| 730 } | |
| 731 } | |
| 732 | |
| 733 /* | |
| 734 * Recalculates current menu's box size | |
| 735 */ | |
| 736 void fghCalculateMenuBoxSize( void ) | |
| 737 { | |
| 738 SFG_MenuEntry* menuEntry; | |
| 739 int width = 0, height = 0; | |
| 740 | |
| 741 /* Make sure there is a current menu set */ | |
| 742 freeglut_return_if_fail( fgStructure.CurrentMenu ); | |
| 743 | |
| 744 /* The menu's box size depends on the menu entries: */ | |
| 745 for( menuEntry = ( SFG_MenuEntry * )fgStructure.CurrentMenu->Entries.First; | |
| 746 menuEntry; | |
| 747 menuEntry = ( SFG_MenuEntry * )menuEntry->Node.Next ) | |
| 748 { | |
| 749 /* Update the menu entry's width value */ | |
| 750 menuEntry->Width = glutBitmapLength( | |
| 751 fgStructure.CurrentMenu->Font, | |
| 752 (unsigned char *)menuEntry->Text | |
| 753 ); | |
| 754 | |
| 755 /* | |
| 756 * If the entry is a submenu, then it needs to be wider to | |
| 757 * accommodate the arrow. | |
| 758 */ | |
| 759 if (menuEntry->SubMenu) | |
| 760 menuEntry->Width += glutBitmapLength( | |
| 761 fgStructure.CurrentMenu->Font, | |
| 762 (unsigned char *)"_" | |
| 763 ); | |
| 764 | |
| 765 /* Check if it's the biggest we've found */ | |
| 766 if( menuEntry->Width > width ) | |
| 767 width = menuEntry->Width; | |
| 768 | |
| 769 height += FREEGLUT_MENUENTRY_HEIGHT(fgStructure.CurrentMenu->Font); | |
| 770 } | |
| 771 | |
| 772 /* Store the menu's box size now: */ | |
| 773 fgStructure.CurrentMenu->Height = height + 2 * FREEGLUT_MENU_BORDER; | |
| 774 fgStructure.CurrentMenu->Width = width + 4 * FREEGLUT_MENU_BORDER; | |
| 775 } | |
| 776 | |
| 777 | |
| 778 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */ | |
| 779 | |
| 780 /* | |
| 781 * Creates a new menu object, adding it to the freeglut structure | |
| 782 */ | |
| 783 int FGAPIENTRY glutCreateMenu( FGCBMenu callback ) | |
| 784 { | |
| 785 /* The menu object creation code resides in fg_structure.c */ | |
| 786 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutCreateMenu" ); | |
| 787 if (fgState.ActiveMenus) | |
| 788 fgError("Menu manipulation not allowed while menus in use."); | |
| 789 | |
| 790 return fgCreateMenu( callback )->ID; | |
| 791 } | |
| 792 | |
| 793 /* | |
| 794 * Destroys a menu object, removing all references to it | |
| 795 */ | |
| 796 void FGAPIENTRY glutDestroyMenu( int menuID ) | |
| 797 { | |
| 798 SFG_Menu* menu; | |
| 799 | |
| 800 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutDestroyMenu" ); | |
| 801 menu = fgMenuByID( menuID ); | |
| 802 | |
| 803 freeglut_return_if_fail( menu ); | |
| 804 if (fgState.ActiveMenus) | |
| 805 fgError("Menu manipulation not allowed while menus in use."); | |
| 806 | |
| 807 /* The menu object destruction code resides in fg_structure.c */ | |
| 808 fgDestroyMenu( menu ); | |
| 809 } | |
| 810 | |
| 811 /* | |
| 812 * Returns the ID number of the currently active menu | |
| 813 */ | |
| 814 int FGAPIENTRY glutGetMenu( void ) | |
| 815 { | |
| 816 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutGetMenu" ); | |
| 817 | |
| 818 if( fgStructure.CurrentMenu ) | |
| 819 return fgStructure.CurrentMenu->ID; | |
| 820 | |
| 821 return 0; | |
| 822 } | |
| 823 | |
| 824 /* | |
| 825 * Sets the current menu given its menu ID | |
| 826 */ | |
| 827 void FGAPIENTRY glutSetMenu( int menuID ) | |
| 828 { | |
| 829 SFG_Menu* menu; | |
| 830 | |
| 831 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSetMenu" ); | |
| 832 menu = fgMenuByID( menuID ); | |
| 833 | |
| 834 freeglut_return_if_fail( menu ); | |
| 835 | |
| 836 fgStructure.CurrentMenu = menu; | |
| 837 } | |
| 838 | |
| 839 /* | |
| 840 * Adds a menu entry to the bottom of the current menu | |
| 841 */ | |
| 842 void FGAPIENTRY glutAddMenuEntry( const char* label, int value ) | |
| 843 { | |
| 844 SFG_MenuEntry* menuEntry; | |
| 845 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutAddMenuEntry" ); | |
| 846 menuEntry = (SFG_MenuEntry *)calloc( sizeof(SFG_MenuEntry), 1 ); | |
| 847 | |
| 848 freeglut_return_if_fail( fgStructure.CurrentMenu ); | |
| 849 if (fgState.ActiveMenus) | |
| 850 fgError("Menu manipulation not allowed while menus in use."); | |
| 851 | |
| 852 menuEntry->Text = strdup( label ); | |
| 853 menuEntry->ID = value; | |
| 854 | |
| 855 /* Have the new menu entry attached to the current menu */ | |
| 856 fgListAppend( &fgStructure.CurrentMenu->Entries, &menuEntry->Node ); | |
| 857 | |
| 858 fghCalculateMenuBoxSize( ); | |
| 859 } | |
| 860 | |
| 861 /* | |
| 862 * Add a sub menu to the bottom of the current menu | |
| 863 */ | |
| 864 void FGAPIENTRY glutAddSubMenu( const char *label, int subMenuID ) | |
| 865 { | |
| 866 SFG_MenuEntry *menuEntry; | |
| 867 SFG_Menu *subMenu; | |
| 868 | |
| 869 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutAddSubMenu" ); | |
| 870 menuEntry = ( SFG_MenuEntry * )calloc( sizeof( SFG_MenuEntry ), 1 ); | |
| 871 subMenu = fgMenuByID( subMenuID ); | |
| 872 | |
| 873 freeglut_return_if_fail( fgStructure.CurrentMenu ); | |
| 874 if (fgState.ActiveMenus) | |
| 875 fgError("Menu manipulation not allowed while menus in use."); | |
| 876 | |
| 877 freeglut_return_if_fail( subMenu ); | |
| 878 | |
| 879 menuEntry->Text = strdup( label ); | |
| 880 menuEntry->SubMenu = subMenu; | |
| 881 menuEntry->ID = -1; | |
| 882 | |
| 883 fgListAppend( &fgStructure.CurrentMenu->Entries, &menuEntry->Node ); | |
| 884 fghCalculateMenuBoxSize( ); | |
| 885 } | |
| 886 | |
| 887 /* | |
| 888 * Changes the current menu's font | |
| 889 */ | |
| 890 void FGAPIENTRY glutSetMenuFont( int menuID, void* fontID ) | |
| 891 { | |
| 892 SFG_Font* font; | |
| 893 SFG_Menu* menu; | |
| 894 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSetMenuFont" ); | |
| 895 menu = fgMenuByID( menuID ); | |
| 896 freeglut_return_if_fail( menu ); | |
| 897 | |
| 898 if (fgState.ActiveMenus) | |
| 899 fgError("Menu manipulation not allowed while menus in use."); | |
| 900 | |
| 901 font = fghFontByID( fontID ); | |
| 902 if (!font) | |
| 903 { | |
| 904 fgWarning("glutChangeMenuFont: bitmap font 0x%08x not found. Make sure you're not passing a stroke font. Ignoring...\n",fontID); | |
| 905 return; | |
| 906 } | |
| 907 | |
| 908 fgStructure.CurrentMenu->Font = fontID; | |
| 909 fghCalculateMenuBoxSize( ); | |
| 910 } | |
| 911 | |
| 912 /* | |
| 913 * Changes the specified menu item in the current menu into a menu entry | |
| 914 */ | |
| 915 void FGAPIENTRY glutChangeToMenuEntry( int item, const char* label, int value ) | |
| 916 { | |
| 917 SFG_MenuEntry* menuEntry = NULL; | |
| 918 | |
| 919 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutChangeToMenuEntry" ); | |
| 920 | |
| 921 freeglut_return_if_fail( fgStructure.CurrentMenu ); | |
| 922 if (fgState.ActiveMenus) | |
| 923 fgError("Menu manipulation not allowed while menus in use."); | |
| 924 | |
| 925 /* Get n-th menu entry in the current menu, starting from one: */ | |
| 926 menuEntry = fghFindMenuEntry( fgStructure.CurrentMenu, item ); | |
| 927 | |
| 928 freeglut_return_if_fail( menuEntry ); | |
| 929 | |
| 930 /* We want it to become a normal menu entry, so: */ | |
| 931 if( menuEntry->Text ) | |
| 932 free( menuEntry->Text ); | |
| 933 | |
| 934 menuEntry->Text = strdup( label ); | |
| 935 menuEntry->ID = value; | |
| 936 menuEntry->SubMenu = NULL; | |
| 937 fghCalculateMenuBoxSize( ); | |
| 938 } | |
| 939 | |
| 940 /* | |
| 941 * Changes the specified menu item in the current menu into a sub-menu trigger. | |
| 942 */ | |
| 943 void FGAPIENTRY glutChangeToSubMenu( int item, const char* label, | |
| 944 int subMenuID ) | |
| 945 { | |
| 946 SFG_Menu* subMenu; | |
| 947 SFG_MenuEntry* menuEntry; | |
| 948 | |
| 949 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutChangeToSubMenu" ); | |
| 950 | |
| 951 freeglut_return_if_fail( fgStructure.CurrentMenu ); | |
| 952 if (fgState.ActiveMenus) | |
| 953 fgError("Menu manipulation not allowed while menus in use."); | |
| 954 | |
| 955 /* Get handle to sub menu */ | |
| 956 subMenu = fgMenuByID( subMenuID ); | |
| 957 menuEntry = NULL; | |
| 958 freeglut_return_if_fail( subMenu ); | |
| 959 | |
| 960 /* Get n-th menu entry in the current menu, starting from one: */ | |
| 961 menuEntry = fghFindMenuEntry( fgStructure.CurrentMenu, item ); | |
| 962 | |
| 963 freeglut_return_if_fail( menuEntry ); | |
| 964 | |
| 965 /* We want it to become a sub menu entry, so: */ | |
| 966 if( menuEntry->Text ) | |
| 967 free( menuEntry->Text ); | |
| 968 | |
| 969 menuEntry->Text = strdup( label ); | |
| 970 menuEntry->SubMenu = subMenu; | |
| 971 menuEntry->ID = -1; | |
| 972 fghCalculateMenuBoxSize( ); | |
| 973 } | |
| 974 | |
| 975 /* | |
| 976 * Removes the specified menu item from the current menu | |
| 977 */ | |
| 978 void FGAPIENTRY glutRemoveMenuItem( int item ) | |
| 979 { | |
| 980 SFG_MenuEntry* menuEntry; | |
| 981 | |
| 982 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutRemoveMenuItem" ); | |
| 983 | |
| 984 freeglut_return_if_fail( fgStructure.CurrentMenu ); | |
| 985 if (fgState.ActiveMenus) | |
| 986 fgError("Menu manipulation not allowed while menus in use."); | |
| 987 | |
| 988 /* Get n-th menu entry in the current menu, starting from one: */ | |
| 989 menuEntry = fghFindMenuEntry( fgStructure.CurrentMenu, item ); | |
| 990 | |
| 991 freeglut_return_if_fail( menuEntry ); | |
| 992 | |
| 993 fgListRemove( &fgStructure.CurrentMenu->Entries, &menuEntry->Node ); | |
| 994 if ( menuEntry->Text ) | |
| 995 free( menuEntry->Text ); | |
| 996 | |
| 997 free( menuEntry ); | |
| 998 fghCalculateMenuBoxSize( ); | |
| 999 } | |
| 1000 | |
| 1001 /* | |
| 1002 * Attaches a menu to the current window | |
| 1003 */ | |
| 1004 void FGAPIENTRY glutAttachMenu( int button ) | |
| 1005 { | |
| 1006 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutAttachMenu" ); | |
| 1007 | |
| 1008 freeglut_return_if_fail( fgStructure.CurrentWindow ); | |
| 1009 | |
| 1010 freeglut_return_if_fail( fgStructure.CurrentMenu ); | |
| 1011 if (fgState.ActiveMenus) | |
| 1012 fgError("Menu manipulation not allowed while menus in use."); | |
| 1013 | |
| 1014 freeglut_return_if_fail( button >= 0 ); | |
| 1015 freeglut_return_if_fail( button < FREEGLUT_MAX_MENUS ); | |
| 1016 | |
| 1017 fgStructure.CurrentWindow->Menu[ button ] = fgStructure.CurrentMenu; | |
| 1018 } | |
| 1019 | |
| 1020 /* | |
| 1021 * Detaches a menu from the current window | |
| 1022 */ | |
| 1023 void FGAPIENTRY glutDetachMenu( int button ) | |
| 1024 { | |
| 1025 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutDetachMenu" ); | |
| 1026 | |
| 1027 freeglut_return_if_fail( fgStructure.CurrentWindow ); | |
| 1028 | |
| 1029 freeglut_return_if_fail( fgStructure.CurrentMenu ); | |
| 1030 if (fgState.ActiveMenus) | |
| 1031 fgError("Menu manipulation not allowed while menus in use."); | |
| 1032 | |
| 1033 freeglut_return_if_fail( button >= 0 ); | |
| 1034 freeglut_return_if_fail( button < FREEGLUT_MAX_MENUS ); | |
| 1035 | |
| 1036 fgStructure.CurrentWindow->Menu[ button ] = NULL; | |
| 1037 } | |
| 1038 | |
| 1039 /* | |
| 1040 * A.Donev: Set and retrieve the menu's user data | |
| 1041 */ | |
| 1042 void* FGAPIENTRY glutGetMenuData( void ) | |
| 1043 { | |
| 1044 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutGetMenuData" ); | |
| 1045 return fgStructure.CurrentMenu->UserData; | |
| 1046 } | |
| 1047 | |
| 1048 void FGAPIENTRY glutSetMenuData(void* data) | |
| 1049 { | |
| 1050 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSetMenuData" ); | |
| 1051 fgStructure.CurrentMenu->UserData=data; | |
| 1052 } | |
| 1053 | |
| 1054 /*** END OF FILE ***/ |
