1 | /* |
2 | * fg_structure.c |
3 | * |
4 | * Windows and menus need tree structure |
5 | * |
6 | * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved. |
7 | * Written by Pawel W. Olszta, <olszta@sourceforge.net> |
8 | * Creation date: Sat Dec 18 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 | #include <GL/freeglut.h> |
29 | #include "fg_internal.h" |
30 | |
31 | /* -- GLOBAL EXPORTS ------------------------------------------------------- */ |
32 | |
33 | /* |
34 | * The SFG_Structure container holds information about windows and menus |
35 | * created between glutInit() and glutMainLoop() return. |
36 | */ |
37 | |
38 | SFG_Structure fgStructure = { { NULL, NULL }, /* The list of windows */ |
39 | { NULL, NULL }, /* The list of menus */ |
40 | { NULL, NULL }, /* Windows to Destroy list */ |
41 | NULL, /* The current window */ |
42 | NULL, /* The current menu */ |
43 | NULL, /* The menu OpenGL context */ |
44 | NULL, /* The game mode window */ |
45 | 0, /* The current new window ID */ |
46 | 0 }; /* The current new menu ID */ |
47 | |
48 | |
49 | /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */ |
50 | |
51 | extern void fgPlatformCreateWindow ( SFG_Window *window ); |
52 | extern void fghDefaultReshape(int width, int height); |
53 | |
54 | static void fghClearCallBacks( SFG_Window *window ) |
55 | { |
56 | if( window ) |
57 | { |
58 | int i; |
59 | for( i = 0; i < TOTAL_CALLBACKS; ++i ) |
60 | window->CallBacks[ i ] = NULL; |
61 | } |
62 | } |
63 | |
64 | /* |
65 | * This private function creates, opens and adds to the hierarchy |
66 | * a freeglut window complete with OpenGL context and stuff... |
67 | * |
68 | * If parent is set to NULL, the window created will be a topmost one. |
69 | */ |
70 | SFG_Window* fgCreateWindow( SFG_Window* parent, const char* title, |
71 | GLboolean positionUse, int x, int y, |
72 | GLboolean sizeUse, int w, int h, |
73 | GLboolean gameMode, GLboolean ) |
74 | { |
75 | /* Have the window object created */ |
76 | SFG_Window *window = (SFG_Window *)calloc( 1, sizeof(SFG_Window) ); |
77 | |
78 | fgPlatformCreateWindow ( window ); |
79 | |
80 | fghClearCallBacks( window ); |
81 | SET_WCB( *window, Reshape, fghDefaultReshape); |
82 | |
83 | /* Initialize the object properties */ |
84 | window->ID = ++fgStructure.WindowID; |
85 | |
86 | fgListInit( &window->Children ); |
87 | if( parent ) |
88 | { |
89 | fgListAppend( &parent->Children, &window->Node ); |
90 | window->Parent = parent; |
91 | } |
92 | else |
93 | fgListAppend( &fgStructure.Windows, &window->Node ); |
94 | |
95 | /* Set the default mouse cursor */ |
96 | window->State.Cursor = GLUT_CURSOR_INHERIT; |
97 | |
98 | /* Mark window as menu if a menu is to be created */ |
99 | window->IsMenu = isMenu; |
100 | |
101 | /* |
102 | * Open the window now. The fgOpenWindow() function is system |
103 | * dependent, and resides in fg_window.c. Uses fgState. |
104 | */ |
105 | fgOpenWindow( window, title, positionUse, x, y, sizeUse, w, h, gameMode, |
106 | (GLboolean)(parent ? GL_TRUE : GL_FALSE) ); |
107 | |
108 | return window; |
109 | } |
110 | |
111 | /* |
112 | * This private function creates a menu and adds it to the menus list |
113 | */ |
114 | SFG_Menu* ( FGCBMenu ) |
115 | { |
116 | SFG_Window *current_window = fgStructure.CurrentWindow; |
117 | |
118 | /* Have the menu object created */ |
119 | SFG_Menu* = (SFG_Menu *)calloc( sizeof(SFG_Menu), 1 ); |
120 | |
121 | menu->ParentWindow = NULL; |
122 | |
123 | /* Create a window for the menu to reside in. */ |
124 | fgCreateWindow( NULL, "freeglut menu" , GL_FALSE, 0, 0, GL_FALSE, 0, 0, |
125 | GL_FALSE, GL_TRUE ); |
126 | menu->Window = fgStructure.CurrentWindow; |
127 | glutDisplayFunc( fgDisplayMenu ); |
128 | |
129 | fgSetWindow( current_window ); |
130 | |
131 | /* Initialize the object properties: */ |
132 | menu->ID = ++fgStructure.MenuID; |
133 | menu->Callback = menuCallback; |
134 | menu->ActiveEntry = NULL; |
135 | menu->Font = fgState.MenuFont; |
136 | |
137 | fgListInit( &menu->Entries ); |
138 | fgListAppend( &fgStructure.Menus, &menu->Node ); |
139 | |
140 | /* Newly created menus implicitly become current ones */ |
141 | fgStructure.CurrentMenu = menu; |
142 | |
143 | return menu; |
144 | } |
145 | |
146 | /* |
147 | * Function to add a window to the linked list of windows to destroy. |
148 | * Subwindows are automatically added because they hang from the window |
149 | * structure. |
150 | */ |
151 | void fgAddToWindowDestroyList( SFG_Window* window ) |
152 | { |
153 | SFG_WindowList *new_list_entry = |
154 | ( SFG_WindowList* )malloc( sizeof(SFG_WindowList ) ); |
155 | new_list_entry->window = window; |
156 | fgListAppend( &fgStructure.WindowsToDestroy, &new_list_entry->node ); |
157 | |
158 | /* Check if the window is the current one... */ |
159 | if( fgStructure.CurrentWindow == window ) |
160 | fgStructure.CurrentWindow = NULL; |
161 | |
162 | /* |
163 | * Clear all window callbacks except Destroy, which will |
164 | * be invoked later. Right now, we are potentially carrying |
165 | * out a freeglut operation at the behest of a client callback, |
166 | * so we are reluctant to re-enter the client with the Destroy |
167 | * callback, right now. The others are all wiped out, however, |
168 | * to ensure that they are no longer called after this point. |
169 | */ |
170 | { |
171 | FGCBDestroy destroy = (FGCBDestroy)FETCH_WCB( *window, Destroy ); |
172 | fghClearCallBacks( window ); |
173 | SET_WCB( *window, Destroy, destroy ); |
174 | } |
175 | } |
176 | |
177 | /* |
178 | * Function to close down all the windows in the "WindowsToDestroy" list |
179 | */ |
180 | void fgCloseWindows( ) |
181 | { |
182 | while( fgStructure.WindowsToDestroy.First ) |
183 | { |
184 | SFG_WindowList *window_ptr = fgStructure.WindowsToDestroy.First; |
185 | fgDestroyWindow( window_ptr->window ); |
186 | fgListRemove( &fgStructure.WindowsToDestroy, &window_ptr->node ); |
187 | free( window_ptr ); |
188 | } |
189 | } |
190 | |
191 | /* |
192 | * This function destroys a window and all of its subwindows. Actually, |
193 | * another function, defined in fg_window.c is called, but this is |
194 | * a whole different story... |
195 | */ |
196 | void fgDestroyWindow( SFG_Window* window ) |
197 | { |
198 | FREEGLUT_INTERNAL_ERROR_EXIT ( window, "Window destroy function called with null window" , |
199 | "fgDestroyWindow" ); |
200 | |
201 | while( window->Children.First ) |
202 | fgDestroyWindow( ( SFG_Window * )window->Children.First ); |
203 | |
204 | { |
205 | SFG_Window *activeWindow = fgStructure.CurrentWindow; |
206 | INVOKE_WCB( *window, Destroy, ( ) ); |
207 | fgSetWindow( activeWindow ); |
208 | } |
209 | |
210 | if( window->Parent ) |
211 | fgListRemove( &window->Parent->Children, &window->Node ); |
212 | else |
213 | fgListRemove( &fgStructure.Windows, &window->Node ); |
214 | |
215 | if( window->ActiveMenu ) |
216 | fgDeactivateMenu( window ); |
217 | |
218 | fghClearCallBacks( window ); |
219 | fgCloseWindow( window ); |
220 | free( window ); |
221 | if( fgStructure.CurrentWindow == window ) |
222 | fgStructure.CurrentWindow = NULL; |
223 | } |
224 | |
225 | /* |
226 | * This is a helper static function that removes a menu (given its pointer) |
227 | * from any windows that can be accessed from a given parent... |
228 | */ |
229 | static void ( SFG_Window* window, SFG_Menu* ) |
230 | { |
231 | SFG_Window *subWindow; |
232 | int i; |
233 | |
234 | /* Check whether this is the active menu in the window */ |
235 | if ( menu == window->ActiveMenu ) |
236 | window->ActiveMenu = NULL ; |
237 | |
238 | /* |
239 | * Check if the menu is attached to the current window, |
240 | * if so, have it detached (by overwriting with a NULL): |
241 | */ |
242 | for( i = 0; i < FREEGLUT_MAX_MENUS; i++ ) |
243 | if( window->Menu[ i ] == menu ) |
244 | window->Menu[ i ] = NULL; |
245 | |
246 | /* Call this function for all of the window's children recursively: */ |
247 | for( subWindow = (SFG_Window *)window->Children.First; |
248 | subWindow; |
249 | subWindow = (SFG_Window *)subWindow->Node.Next) |
250 | fghRemoveMenuFromWindow( subWindow, menu ); |
251 | } |
252 | |
253 | /* |
254 | * This is a static helper function that removes menu references |
255 | * from another menu, given two pointers to them... |
256 | */ |
257 | static void ( SFG_Menu* from, SFG_Menu* ) |
258 | { |
259 | SFG_MenuEntry *entry; |
260 | |
261 | for( entry = (SFG_MenuEntry *)from->Entries.First; |
262 | entry; |
263 | entry = ( SFG_MenuEntry * )entry->Node.Next ) |
264 | if( entry->SubMenu == menu ) |
265 | entry->SubMenu = NULL; |
266 | } |
267 | |
268 | /* |
269 | * This function destroys a menu specified by the parameter. All menus |
270 | * and windows are updated to make sure no ill pointers hang around. |
271 | */ |
272 | void ( SFG_Menu* ) |
273 | { |
274 | SFG_Window *window; |
275 | SFG_Menu *from; |
276 | |
277 | FREEGLUT_INTERNAL_ERROR_EXIT ( menu, "Menu destroy function called with null menu" , |
278 | "fgDestroyMenu" ); |
279 | |
280 | /* First of all, have all references to this menu removed from all windows: */ |
281 | for( window = (SFG_Window *)fgStructure.Windows.First; |
282 | window; |
283 | window = (SFG_Window *)window->Node.Next ) |
284 | fghRemoveMenuFromWindow( window, menu ); |
285 | |
286 | /* Now proceed with removing menu entries that lead to this menu */ |
287 | for( from = ( SFG_Menu * )fgStructure.Menus.First; |
288 | from; |
289 | from = ( SFG_Menu * )from->Node.Next ) |
290 | fghRemoveMenuFromMenu( from, menu ); |
291 | |
292 | /* |
293 | * If the programmer defined a destroy callback, call it |
294 | * A. Donev: But first make this the active menu |
295 | */ |
296 | if( menu->Destroy ) |
297 | { |
298 | SFG_Menu *=fgStructure.CurrentMenu; |
299 | fgStructure.CurrentMenu = menu; |
300 | menu->Destroy( ); |
301 | fgStructure.CurrentMenu = activeMenu; |
302 | } |
303 | |
304 | /* |
305 | * Now we are pretty sure the menu is not used anywhere |
306 | * and that we can remove all of its entries |
307 | */ |
308 | while( menu->Entries.First ) |
309 | { |
310 | SFG_MenuEntry *entry = ( SFG_MenuEntry * ) menu->Entries.First; |
311 | |
312 | fgListRemove( &menu->Entries, &entry->Node ); |
313 | |
314 | if( entry->Text ) |
315 | free( entry->Text ); |
316 | entry->Text = NULL; |
317 | |
318 | free( entry ); |
319 | } |
320 | |
321 | if( fgStructure.CurrentWindow == menu->Window ) |
322 | fgSetWindow( NULL ); |
323 | fgDestroyWindow( menu->Window ); |
324 | fgListRemove( &fgStructure.Menus, &menu->Node ); |
325 | if( fgStructure.CurrentMenu == menu ) |
326 | fgStructure.CurrentMenu = NULL; |
327 | |
328 | free( menu ); |
329 | } |
330 | |
331 | /* |
332 | * This function should be called on glutInit(). It will prepare the internal |
333 | * structure of freeglut to be used in the application. The structure will be |
334 | * destroyed using fgDestroyStructure() on glutMainLoop() return. In that |
335 | * case further use of freeglut should be preceded with a glutInit() call. |
336 | */ |
337 | void fgCreateStructure( void ) |
338 | { |
339 | /* |
340 | * We will be needing two lists: the first containing windows, |
341 | * and the second containing the user-defined menus. |
342 | * Also, no current window/menu is set, as none has been created yet. |
343 | */ |
344 | |
345 | fgListInit(&fgStructure.Windows); |
346 | fgListInit(&fgStructure.Menus); |
347 | fgListInit(&fgStructure.WindowsToDestroy); |
348 | |
349 | fgStructure.CurrentWindow = NULL; |
350 | fgStructure.CurrentMenu = NULL; |
351 | fgStructure.MenuContext = NULL; |
352 | fgStructure.GameModeWindow = NULL; |
353 | fgStructure.WindowID = 0; |
354 | fgStructure.MenuID = 0; |
355 | } |
356 | |
357 | /* |
358 | * This function is automatically called on glutMainLoop() return. |
359 | * It should deallocate and destroy all remnants of previous |
360 | * glutInit()-enforced structure initialization... |
361 | */ |
362 | void fgDestroyStructure( void ) |
363 | { |
364 | /* Clean up the WindowsToDestroy list. */ |
365 | fgCloseWindows( ); |
366 | |
367 | /* Make sure all windows and menus have been deallocated */ |
368 | while( fgStructure.Menus.First ) |
369 | fgDestroyMenu( ( SFG_Menu * )fgStructure.Menus.First ); |
370 | |
371 | while( fgStructure.Windows.First ) |
372 | fgDestroyWindow( ( SFG_Window * )fgStructure.Windows.First ); |
373 | } |
374 | |
375 | /* |
376 | * Helper function to enumerate through all registered top-level windows |
377 | */ |
378 | void fgEnumWindows( FGCBWindowEnumerator enumCallback, SFG_Enumerator* enumerator ) |
379 | { |
380 | SFG_Window *window; |
381 | |
382 | FREEGLUT_INTERNAL_ERROR_EXIT ( enumCallback && enumerator, |
383 | "Enumerator or callback missing from window enumerator call" , |
384 | "fgEnumWindows" ); |
385 | |
386 | /* Check every of the top-level windows */ |
387 | for( window = ( SFG_Window * )fgStructure.Windows.First; |
388 | window; |
389 | window = ( SFG_Window * )window->Node.Next ) |
390 | { |
391 | enumCallback( window, enumerator ); |
392 | if( enumerator->found ) |
393 | return; |
394 | } |
395 | } |
396 | |
397 | /* |
398 | * Helper function to enumerate through all registered top-level windows |
399 | */ |
400 | void ( FGCBMenuEnumerator enumCallback, SFG_Enumerator* enumerator ) |
401 | { |
402 | SFG_Menu *; |
403 | |
404 | FREEGLUT_INTERNAL_ERROR_EXIT ( enumCallback && enumerator, |
405 | "Enumerator or callback missing from window enumerator call" , |
406 | "fgEnumWindows" ); |
407 | |
408 | /* It's enough to check all entries in fgStructure.Menus... */ |
409 | for( menu = (SFG_Menu *)fgStructure.Menus.First; |
410 | menu; |
411 | menu = (SFG_Menu *)menu->Node.Next ) |
412 | { |
413 | enumCallback( menu, enumerator ); |
414 | if( enumerator->found ) |
415 | return; |
416 | } |
417 | } |
418 | |
419 | /* |
420 | * Helper function to enumerate through all a window's subwindows |
421 | * (single level descent) |
422 | */ |
423 | void fgEnumSubWindows( SFG_Window* window, FGCBWindowEnumerator enumCallback, |
424 | SFG_Enumerator* enumerator ) |
425 | { |
426 | SFG_Window *child; |
427 | |
428 | FREEGLUT_INTERNAL_ERROR_EXIT ( enumCallback && enumerator, |
429 | "Enumerator or callback missing from subwindow enumerator call" , |
430 | "fgEnumSubWindows" ); |
431 | FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Window Enumeration" ); |
432 | |
433 | for( child = ( SFG_Window * )window->Children.First; |
434 | child; |
435 | child = ( SFG_Window * )child->Node.Next ) |
436 | { |
437 | enumCallback( child, enumerator ); |
438 | if( enumerator->found ) |
439 | return; |
440 | } |
441 | } |
442 | |
443 | /* |
444 | * A static helper function to look for a window given its handle |
445 | */ |
446 | static void fghcbWindowByHandle( SFG_Window *window, |
447 | SFG_Enumerator *enumerator ) |
448 | { |
449 | if ( enumerator->found ) |
450 | return; |
451 | |
452 | /* Check the window's handle. Hope this works. Looks ugly. That's for sure. */ |
453 | if( window->Window.Handle == (SFG_WindowHandleType) (enumerator->data) ) |
454 | { |
455 | enumerator->found = GL_TRUE; |
456 | enumerator->data = window; |
457 | |
458 | return; |
459 | } |
460 | |
461 | /* Otherwise, check this window's children */ |
462 | fgEnumSubWindows( window, fghcbWindowByHandle, enumerator ); |
463 | } |
464 | |
465 | /* |
466 | * fgWindowByHandle returns a (SFG_Window *) value pointing to the |
467 | * first window in the queue matching the specified window handle. |
468 | * The function is defined in fg_structure.c file. |
469 | */ |
470 | SFG_Window* fgWindowByHandle ( SFG_WindowHandleType hWindow ) |
471 | { |
472 | SFG_Enumerator enumerator; |
473 | |
474 | /* This is easy and makes use of the windows enumeration defined above */ |
475 | enumerator.found = GL_FALSE; |
476 | enumerator.data = (void *)hWindow; |
477 | fgEnumWindows( fghcbWindowByHandle, &enumerator ); |
478 | |
479 | if( enumerator.found ) |
480 | return( SFG_Window *) enumerator.data; |
481 | return NULL; |
482 | } |
483 | |
484 | /* |
485 | * A static helper function to look for a window given its ID |
486 | */ |
487 | static void fghcbWindowByID( SFG_Window *window, SFG_Enumerator *enumerator ) |
488 | { |
489 | /* Make sure we do not overwrite our precious results... */ |
490 | if( enumerator->found ) |
491 | return; |
492 | |
493 | /* Check the window's handle. Hope this works. Looks ugly. That's for sure. */ |
494 | if( window->ID == *( int *)(enumerator->data) ) |
495 | { |
496 | enumerator->found = GL_TRUE; |
497 | enumerator->data = window; |
498 | |
499 | return; |
500 | } |
501 | |
502 | /* Otherwise, check this window's children */ |
503 | fgEnumSubWindows( window, fghcbWindowByID, enumerator ); |
504 | } |
505 | |
506 | /* |
507 | * This function is similar to the previous one, except it is |
508 | * looking for a specified (sub)window identifier. The function |
509 | * is defined in fg_structure.c file. |
510 | */ |
511 | SFG_Window* fgWindowByID( int windowID ) |
512 | { |
513 | SFG_Enumerator enumerator; |
514 | |
515 | /* Uses a method very similar for fgWindowByHandle... */ |
516 | enumerator.found = GL_FALSE; |
517 | enumerator.data = ( void * )&windowID; |
518 | fgEnumWindows( fghcbWindowByID, &enumerator ); |
519 | if( enumerator.found ) |
520 | return ( SFG_Window * )enumerator.data; |
521 | return NULL; |
522 | } |
523 | |
524 | /* |
525 | * A static helper function to look for a menu given its ID |
526 | */ |
527 | static void ( SFG_Menu *, |
528 | SFG_Enumerator *enumerator ) |
529 | { |
530 | if ( enumerator->found ) |
531 | return; |
532 | |
533 | /* Check the menu's ID. */ |
534 | if( menu->ID == *( int *)(enumerator->data) ) |
535 | { |
536 | enumerator->found = GL_TRUE; |
537 | enumerator->data = menu; |
538 | |
539 | return; |
540 | } |
541 | } |
542 | |
543 | /* |
544 | * Looks up a menu given its ID. This is easier than fgWindowByXXX |
545 | * as all menus are placed in one doubly linked list... |
546 | */ |
547 | SFG_Menu* ( int ) |
548 | { |
549 | SFG_Enumerator enumerator; |
550 | |
551 | /* This is easy and makes use of the menus enumeration defined above */ |
552 | enumerator.found = GL_FALSE; |
553 | enumerator.data = (void *)&menuID; |
554 | fgEnumMenus( fghcbMenuByID, &enumerator ); |
555 | |
556 | if( enumerator.found ) |
557 | return( SFG_Menu *) enumerator.data; |
558 | |
559 | return NULL; |
560 | } |
561 | |
562 | /* |
563 | * A static helper function to look for an active menu |
564 | */ |
565 | static void ( SFG_Menu *, |
566 | SFG_Enumerator *enumerator ) |
567 | { |
568 | if ( enumerator->found ) |
569 | return; |
570 | |
571 | /* Check the menu's is active */ |
572 | if( menu->IsActive ) |
573 | { |
574 | enumerator->found = GL_TRUE; |
575 | enumerator->data = menu; |
576 | |
577 | return; |
578 | } |
579 | } |
580 | |
581 | /* |
582 | * Returns active menu, if any. Assumption: only one menu active throughout application at any one time. |
583 | * This is false when a submenu is also open. |
584 | * This is easier than fgWindowByXXX as all menus are placed in one doubly linked list... |
585 | */ |
586 | SFG_Menu* ( ) |
587 | { |
588 | SFG_Enumerator enumerator; |
589 | |
590 | /* This is easy and makes use of the menus enumeration defined above */ |
591 | enumerator.found = GL_FALSE; |
592 | fgEnumMenus( fghcbGetActiveMenu, &enumerator ); |
593 | |
594 | if( enumerator.found ) |
595 | return( SFG_Menu *) enumerator.data; |
596 | |
597 | return NULL; |
598 | } |
599 | |
600 | /* |
601 | * List functions... |
602 | */ |
603 | void fgListInit(SFG_List *list) |
604 | { |
605 | list->First = NULL; |
606 | list->Last = NULL; |
607 | } |
608 | |
609 | void fgListAppend(SFG_List *list, SFG_Node *node) |
610 | { |
611 | if ( list->Last ) |
612 | { |
613 | SFG_Node *ln = (SFG_Node *) list->Last; |
614 | ln->Next = node; |
615 | node->Prev = ln; |
616 | } |
617 | else |
618 | { |
619 | node->Prev = NULL; |
620 | list->First = node; |
621 | } |
622 | |
623 | node->Next = NULL; |
624 | list->Last = node; |
625 | } |
626 | |
627 | void fgListRemove(SFG_List *list, SFG_Node *node) |
628 | { |
629 | if( node->Next ) |
630 | ( ( SFG_Node * )node->Next )->Prev = node->Prev; |
631 | if( node->Prev ) |
632 | ( ( SFG_Node * )node->Prev )->Next = node->Next; |
633 | if( ( ( SFG_Node * )list->First ) == node ) |
634 | list->First = node->Next; |
635 | if( ( ( SFG_Node * )list->Last ) == node ) |
636 | list->Last = node->Prev; |
637 | } |
638 | |
639 | int fgListLength(SFG_List *list) |
640 | { |
641 | SFG_Node *node; |
642 | int length = 0; |
643 | |
644 | for( node =( SFG_Node * )list->First; |
645 | node; |
646 | node = ( SFG_Node * )node->Next ) |
647 | ++length; |
648 | |
649 | return length; |
650 | } |
651 | |
652 | |
653 | void fgListInsert(SFG_List *list, SFG_Node *next, SFG_Node *node) |
654 | { |
655 | SFG_Node *prev; |
656 | |
657 | if( (node->Next = next) ) |
658 | { |
659 | prev = next->Prev; |
660 | next->Prev = node; |
661 | } |
662 | else |
663 | { |
664 | prev = list->Last; |
665 | list->Last = node; |
666 | } |
667 | |
668 | if( (node->Prev = prev) ) |
669 | prev->Next = node; |
670 | else |
671 | list->First = node; |
672 | } |
673 | |
674 | /*** END OF FILE ***/ |
675 | |