]> icculus.org git repositories - divverent/nexuiz.git/blob - scmenu/source/system/structure.qc
This was certainly the wrong solution to a problem I dont see.
[divverent/nexuiz.git] / scmenu / source / system / structure.qc
1 // DP/Nex Menu
2 // system/structure.qc
3
4 // TODO: finish the debug output
5 void( float pLevel, string pText ) _Menu_Structure_Debug =
6 {
7         if( pLevel <= sys_debug_structure )
8                 print( pText );
9 };
10
11 void( bool pUser ) _Menu_Select =
12 {
13         Raise_Select( Menu_ActiveItem, true, pUser );
14 };
15
16 entity( entity pItem ) _Menu_GetParent =
17 {
18         if( !pItem._parent )
19                 return null_entity;
20         if( Menu_IsEmbedded( pItem._parent ) )
21                 return _Menu_GetParent( pItem._parent );
22         return pItem._parent;
23 };
24
25 bool( entity pItem, entity pParent ) _Menu_IsEmbeddedParentOf =
26 {
27         if( pItem._parent == pParent )
28                 return true;
29         if( Menu_IsEmbedded( pItem._parent ) )
30                 return _Menu_IsEmbeddedParentOf( pItem._parent, pParent );
31         return false;
32 };
33
34 entity( entity pItem ) _Menu_GetFirst =
35 {
36         if( Menu_IsEmbedded( pItem ) && pItem._child )
37                 return _Menu_GetFirst( pItem._child );
38         return pItem;
39 };
40
41 entity( entity pItem ) _Menu_GetLast =
42 {
43         if( Menu_IsEmbedded( pItem ) && pItem._child ) {
44                 local entity lNode;
45
46                 for( lNode = pItem._child ; lNode._next ; lNode = lNode._next );
47                 return _Menu_GetLast( lNode );
48         }
49         return pItem;
50 };
51
52 entity( entity pItem ) _Menu_GetNext =
53 {
54         local entity lNext;
55
56         lNext = pItem._next;
57         if( lNext )
58                 return _Menu_GetFirst( lNext );
59         if( Menu_IsEmbedded( pItem._parent ) && pItem._parent != Menu_ActiveWindow )
60                 return _Menu_GetNext( pItem._parent );
61         else
62                 return null_entity;
63 };
64
65 entity( entity pItem ) _Menu_GetPrev =
66 {
67         local entity lPrev;
68
69         lPrev = pItem._prev;
70         if( lPrev )
71                 return _Menu_GetLast( lPrev );
72         if( Menu_IsEmbedded( pItem._parent ) && pItem._parent != Menu_ActiveWindow )
73                 return _Menu_GetPrev( pItem._parent );
74         else
75                 return null_entity;
76 };
77
78 void() _Menu_SelectNext =
79 {
80         local entity lTemp;
81
82         if( !Menu_ActiveItem ) {
83                 _Menu_Structure_Debug( 1, "_SelectNext: Bad Menu_ActiveItem!\n" );
84                 return;
85         }
86
87         // try to select the next item
88         lTemp = Menu_ActiveItem;
89         while( (lTemp = _Menu_GetNext( lTemp )) != null_entity )
90                         if( Menu_IsSelectable( lTemp ) ) {
91                         Menu_ActiveItem = lTemp;
92                         _Menu_Structure_Debug( 1, strcat( "_SelectNext: ", lTemp.name, "\n" ) );
93                         return;
94                 }
95         // loop
96         // only because of embedded:
97         for( lTemp = Menu_ActiveItem ; _Menu_GetPrev( lTemp ) ; lTemp = _Menu_GetPrev( lTemp ) );
98 // TODO: rewrite _Menu_Select* to use an additional temp variable for storing the result of the functionc all
99         for( ; lTemp != Menu_ActiveItem ; lTemp = _Menu_GetNext( lTemp ) )
100                 if( Menu_IsSelectable( lTemp ) ) {
101                         Menu_ActiveItem = lTemp;
102                         _Menu_Structure_Debug( 1, strcat( "_SelectNext after loop: ", lTemp.name, "\n" ) );
103                         return;
104                 }
105 };
106
107 void() _Menu_SelectPrev =
108 {
109         local entity lTemp;
110
111         if( !Menu_ActiveItem ) {
112                 _Menu_Structure_Debug( 1, "_SelectPrev: Bad Menu_ActiveItem!\n" );
113                 return;
114         }
115
116         // try to select the previous item
117         lTemp = Menu_ActiveItem;
118         while( (lTemp = _Menu_GetPrev( lTemp )) != null_entity )
119                 if( Menu_IsSelectable( lTemp ) ) {
120                         Menu_ActiveItem = lTemp;
121                         _Menu_Structure_Debug( 1, strcat( "_SelectPrev: ", lTemp.name, "\n" ) );
122                         return;
123                 }
124         // loop
125         for( lTemp = Menu_ActiveItem ; _Menu_GetNext( lTemp ) ; lTemp = _Menu_GetNext( lTemp ) );
126         for( ; lTemp != Menu_ActiveItem ; lTemp = _Menu_GetPrev( lTemp ) ) {
127                 if( Menu_IsSelectable( lTemp ) ) {
128                         Menu_ActiveItem = lTemp;
129                         _Menu_Structure_Debug( 1, strcat( "_SelectPrev after loop: ", lTemp.name, "\n" ) );
130                         return;
131                 }
132         }
133 };
134
135 bool() _Menu_SelectUp =
136 {
137         // Menu_ActiveItem is the child
138         local entity lSelected, lParent, lNode;
139
140         lSelected = Menu_ActiveItem;
141         if( !lSelected ) {
142                 _Menu_Structure_Debug( 1, "_SelectUp: Bad Menu_ActiveItem!\n" );
143                 return false;
144         }
145
146         // If we try to select up the active window, we'll pop the menu history
147         if( lSelected == Menu_ActiveWindow ) {
148                 // If there is no history, we quit the menu
149                 _Menu_Structure_Debug( 2, "_SelectUp: Selecting up current active window..\n" );
150                 if( Menu_History == null_entity ) {
151                         _Menu_Structure_Debug( 2, "_SelectUp: Empty history -> toggling menu..\n" );
152                         if( !Menu_Toggle() )
153                                 Menu_Reselect( false );
154                         return true;
155                 }
156
157                 _Menu_Structure_Debug( 2, "_SelectUp: Popping history..\n" );
158                 Menu_History_Pop();
159         }
160
161         lParent = _Menu_GetParent( lSelected );
162         if( !lParent ) {
163                 _Menu_Structure_Debug( 2, "_SelectUp: No parent and not active window!\n" );
164                 return false;
165         }
166
167         // If this window is selectable, we know that there is at least one selectable item in the parent,
168         // thus we will select the parent
169         if( Menu_IsSelectable( lParent ) ) {
170                 Menu_ActiveItem = lParent;
171                 _Menu_Structure_Debug( 1, strcat( "_SelectUp: first parent: ", lParent.name, "\n" ) );
172                 return true;
173         }
174
175         // if there is no parent window of this window (lParent), it's the active window
176         // else we have failed
177         if( lParent == Menu_ActiveWindow ) {
178                 Menu_ActiveItem = Menu_ActiveWindow;
179                 _Menu_Structure_Debug( 2, strcat( "_SelectUp: select up parent: ", Menu_ActiveItem.name, "\n" ) );
180                 if( _Menu_SelectUp() )
181                         return true;
182                 Menu_ActiveItem = lSelected;
183                 return false;
184         } else if( !lParent._parent ) {
185                 _Menu_Structure_Debug( 1, "_SelectUp: No parent of parent and not active window!\n" );
186                 return false;
187         }
188
189         // If not, we try to determine whether the window is the first window with a selectable children in
190         // the parent window. If lParent is the selected by selectdown, we move up, if not we have found it.
191         // IDEA: inline this
192         Menu_ActiveItem = _Menu_GetParent( lParent );
193         _Menu_Structure_Debug( 2, strcat( "_SelectUp: SelectDown on parent of parent '", Menu_ActiveItem.name, "' \n" ) );
194         _Menu_SelectDown();
195         // thanks to embedded windows (and not only them) - added later on - perhaps doesnt really
196         // fit into the old logic behind it - take this with caution
197         for( lNode = Menu_ActiveItem ; lNode ; lNode = lNode._parent )
198                 if( lNode._parent == lParent ) {
199                         Menu_ActiveItem = _Menu_GetParent( lSelected ); //lParent._parent;
200                         if( _Menu_SelectUp() )
201                                 return true;
202                         Menu_ActiveItem = lSelected;
203                         return false;
204                 }
205
206         // else we have already found the window we have searched!
207         return true;
208 };
209
210 void( entity pItem ) _Menu_PrintRunFlag;
211 bool() _Menu_SelectDown =
212 {
213         // Menu_ActiveItem is the window
214         local entity lParent, lChild;
215
216         lParent = Menu_ActiveItem;
217         if( !lParent ) {
218                 _Menu_Structure_Debug( 1, "_SelectDown: Bad Menu_ActiveItem!\n" );
219                 return false;
220         }
221
222         // lets find the first selectable item
223         for( lChild = _Menu_GetFirst( lParent._child ) ; lChild ; lChild = _Menu_GetNext( lChild ) )
224                 if( Menu_IsSelectable( lChild ) ) {
225                         Menu_ActiveItem = lChild;
226                         _Menu_PrintRunFlag( lChild );
227                         _Menu_Structure_Debug( 1, strcat( "_SelectDown: ", lChild.name, "\n" ) );
228                         return true;
229                 }
230
231         // lets find the first window that has a selectable item
232         for( lChild = _Menu_GetFirst( lParent._child ) ; lChild ; lChild = _Menu_GetNext( lChild ) )
233                 if( !Menu_IsEmbedded( lChild ) ) {
234                         Menu_ActiveItem = lChild;
235                         _Menu_Structure_Debug( 2, strcat( "_SelectDown: Try child: ", Menu_ActiveItem.name, "\n" ) );
236                         if( _Menu_SelectDown() )
237                                 return true;
238                 }
239
240         Menu_ActiveItem = lParent;
241         return false;
242 };
243
244 void() _Menu_Reselect =
245 {
246         Menu_ActiveItem = Menu_ActiveWindow;
247         _Menu_SelectDown();
248 };
249
250 void( bool pUser ) Menu_SelectNext =
251 {
252         Raise_Select( Menu_ActiveItem, false, pUser );
253         _Menu_SelectNext();
254         Raise_Select( Menu_ActiveItem, true, pUser );
255 };
256
257 void( bool pUser ) Menu_SelectPrev =
258 {
259         Raise_Select( Menu_ActiveItem, false, pUser );
260         _Menu_SelectPrev();
261         Raise_Select( Menu_ActiveItem, true, pUser );
262 };
263
264 bool( bool pUser ) Menu_SelectUp =
265 {
266         local entity lOld;
267
268         lOld = Menu_ActiveItem;
269         if( _Menu_SelectUp() ) {
270                 Raise_Select( lOld, false, pUser );
271                 Raise_Select( Menu_ActiveItem, true, pUser );
272                 return true;
273         }
274         return false;
275 };
276
277 bool( bool pUser ) Menu_SelectDown =
278 {
279         local entity lOld;
280
281         lOld = Menu_ActiveItem;
282         if( _Menu_SelectDown() ) {
283                 Raise_Select( lOld, false, pUser );
284                 Raise_Select( Menu_ActiveItem, true, pUser );
285                 return true;
286         }
287         return false;
288 };
289
290 void( entity pItem, bool pUser ) Menu_Select =
291 {
292         Raise_Select( Menu_ActiveItem, false, pUser );
293         _Menu_Structure_Debug( 1, strcat( "Menu_Select: ", pItem.name, "\n" ) );
294         Menu_ActiveItem = pItem;
295         Raise_Select( Menu_ActiveItem, true, pUser );
296 };
297
298 void( entity pItem, bool pUser ) Menu_CorrectSelection =
299 {
300         if( Menu_ActiveItem != pItem )
301                 Menu_Select( pItem, pUser );
302 };
303
304 void( bool pUser ) Menu_Reselect =
305 {
306         Raise_Select( Menu_ActiveItem, false, pUser );
307         _Menu_Reselect();
308         Raise_Select( Menu_ActiveItem, true, pUser );
309 };
310
311 void( entity pMenu, bool pMakeActive, bool pUser ) Menu_JumpToWindow =
312 {
313         Raise_Select( Menu_ActiveItem, false, pUser );
314
315         // only jump to windows
316         if( !pMenu._child )
317                 error("Cant jump to ", pMenu.name, " !\n");
318
319         // add a history point
320         if( pMakeActive ) {
321                 Menu_History_Push( pMenu, Util_NullFunction );
322                 Menu_ActiveWindow = pMenu;
323         }
324
325         // now set the selected to the first selectable child
326         Menu_ActiveItem = pMenu;
327         if( !_Menu_SelectDown() )
328                 error( "Couldn't jump to ", pMenu.name, " !\n" );
329
330         Raise_Select( Menu_ActiveItem, true, pUser );
331 };
332
333 #ifdef USEFUNCTIONS
334 bool( entity pEntity, float pFlag ) Menu_HasFlag =
335 {
336         if( pEntity.flag & pFlag )
337                 return true;
338         return false;
339 };
340
341 bool( entity pEntity, float pRunFlag ) Menu_HasRunFlag =
342 {
343         if( pEntity._runFlag & pRunFlag )
344                 return true;
345         return false;
346 };
347 #endif
348
349 entity( entity pOrigin, string pName, bool pThrow ) Menu_GetItemEx =
350 {
351         local entity lItem;
352
353         // FIXME: perhaps add another function or do this in some other way
354         if( substring( pName, 0, 2 ) == "::" )
355                 lItem = findstring( null_entity,  name, substring( pName, 2, 100000 ) );
356         // support for direction tokens, init token ##
357         else if( substring( pName, 0, 2 ) == "##" ) {
358                 local string lToken;
359                 local float lCount, lCounter;
360                 lItem = pOrigin;
361                 lCount = tokenize( substring( pName, 2, 100000 ) );
362                 // we have the following tokens atfer the ##: up down next prev
363                 for( lCounter = 0 ; lCounter < lCount && lItem ; ++lCounter ) {
364                         lToken = argv( lCounter );
365                         if( lToken == "up" )
366                                 lItem = lItem._parent;
367                         else if( lToken == "down" )
368                                 lItem = lItem._child;
369                         else if( lToken == "next" )
370                                 lItem = lItem._next;
371                         else if( lToken == "prev" )
372                                 lItem = lItem._prev;
373                         else
374                                 error( "Bad direction link(bad token): '", pName, "'!" );
375                 }
376         } else  {
377                 // we start from the current namespace and try to find the object
378                 // by checking for it in all parent namespaces
379                 lItem = null_entity;
380                 while( !lItem && (pOrigin = pOrigin._parent) != null_entity )
381                         lItem = findstring( null_entity, name, strcat( pOrigin.name, "::", pName ) );
382
383                 if( !lItem )
384                         lItem = findstring( null_entity, name, pName );
385         }
386
387         if( lItem == null_entity && pThrow )
388                 error( "Couldn't find item '", pName, "'!" );
389
390         return lItem;
391 };
392
393 entity( entity pOrigin, string pName, bool pThrow ) Menu_GetChildEx =
394 {
395         local entity lItem;
396
397         if( pOrigin )
398                 lItem = findstring( null_entity, name, strcat( pOrigin.name, "::", pName ) );
399         else
400                 lItem = findstring( null_entity, name, pName );
401
402         if( lItem == null_entity && pThrow )
403                 error( "Couldn't find item '", pName, "'!" );
404
405         return lItem;
406 };
407
408 entity( string pName ) Menu_GetItem =
409 {
410         return Menu_GetItemEx( self, pName, true );
411 };
412
413 entity( string pName ) Menu_GetChild =
414 {
415         return Menu_GetChildEx( self, pName, true );
416 };
417
418 void( entity pWindow ) Menu_EmptyWindow =
419 {
420         local entity lChild;
421
422         for( lChild = pWindow._child ; lChild ; lChild = lChild._next ) {
423                 Menu_EmptyWindow( lChild );
424                 Raise_Destroy( lChild );
425                 remove( lChild );
426         }
427
428         pWindow._child = null_entity;
429 };
430
431 void( entity pEntity ) Menu_RemoveItem =
432 {
433         local entity lParent;
434         // raise the destroy event
435         lParent = pEntity._parent;
436         Menu_EmptyWindow( pEntity );
437         Raise_Destroy( pEntity );
438         remove( pEntity );
439         if( lParent )
440                 Menu_LinkChildren( lParent );
441 };
442
443 void( entity pItem ) _Menu_PrintRunFlag =
444 {
445         if( sys_debug_runflag ) {
446                 print( " ", pItem.name, " Runflags: " );
447                 if( pItem._runFlag & RUNFLAG_TEMPLATE )
448                         print( "TEMPLATE " );
449                 if( pItem._runFlag & RUNFLAG_MOUSEINAREA )
450                         print( "MOUSEINAREA " );
451                 if( pItem._runFlag & RUNFLAG_HADMOUSE )
452                         print( "HADMOUSE " );
453                 if( pItem._runFlag & RUNFLAG_CHILDDRAWONLY )
454                         print( "CHILDDRAWONLY " );
455                 if( pItem._runFlag & RUNFLAG_CHILDDRAWUPDATEONLY )
456                         print( "CHILDDRAWUPDATEONLY " );
457                 if( pItem._runFlag & RUNFLAG_HIDDEN )
458                         print( "HIDDEN " );
459                 if( pItem._runFlag & RUNFLAG_CLIPPED )
460                         print( "CLIPPED " );
461                 if( pItem._runFlag & RUNFLAG_NOSELECT )
462                         print( "NOSELECT " );
463                 print( "\n" );
464         }
465 };
466
467 void( entity pItem ) Menu_SetRunFlag =
468 {
469         // RUNFLAG_TEMPLATE
470         if( pItem.flag & FLAG_TEMPLATE )
471                 pItem._runFlag = pItem._runFlag | RUNFLAG_TEMPLATE;
472         // RUNFLAG_HADMOUSE,
473         if( pItem._runFlag & RUNFLAG_MOUSEINAREA )
474                 pItem._runFlag = (pItem._runFlag - RUNFLAG_MOUSEINAREA) | RUNFLAG_HADMOUSE;
475         // RUNFLAG_MOUSEINAREA,
476         // these will be handled in MENU_PROCESS_MOUSE
477         // RUNFLAG_CHILDDRAWONLY,
478         // RUNFLAG_CHILDDRAWONLY,
479         // these two are handled in InheritRunFlag
480         // RUNFLAG_CLIPPED,
481         // check if it is within the clipping area (ie. really visible)
482         // trick: since the position and size are clipped against the previous clipping area
483         // Menu_Clip_Size will be '0 0 0', if the areas dont overlap
484         if( Menu_Clip_Size == '0 0 0' && Menu_Clip_Position != '0 0 0' )
485                 pItem._runFlag = pItem._runFlag | RUNFLAG_CLIPPED;
486         // RUNFLAG_HIDDEN
487         if( ( pItem.flag & FLAG_HIDDEN  ) ||
488             ( pItem._runFlag & RUNFLAG_TEMPLATE ) ||
489             ( ( pItem.flag & FLAG_SERVERONLY ) && !( gamestatus & GAME_ISSERVER ) ) ||
490             ( ( pItem.flag & FLAG_CONNECTEDONLY ) && !( gamestatus & GAME_CONNECTED ) ) ||
491             ( ( pItem.flag & FLAG_DEVELOPERONLY ) && !( gamestatus & GAME_DEVELOPER ) ) )
492                 pItem._runFlag = pItem._runFlag | RUNFLAG_HIDDEN;
493         // RUNFLAG_NOSELECT
494         if( ( pItem.flag & FLAG_NOSELECT ) ||
495             ( pItem.flag & FLAG_DRAWONLY ) ||
496             ( pItem.flag & FLAG_DRAWUPDATEONLY ) ||
497             ( pItem.flag & FLAG_EMBEDDED ) ||
498             ( pItem._runFlag & RUNFLAG_TEMPLATE ) ||
499             ( pItem._runFlag & RUNFLAG_HIDDEN ) ||
500             ( pItem._runFlag & RUNFLAG_CHILDDRAWONLY ) ||
501             ( pItem._runFlag & RUNFLAG_CHILDDRAWUPDATEONLY ) )
502                 pItem._runFlag = pItem._runFlag | RUNFLAG_NOSELECT;
503
504         _Menu_PrintRunFlag( pItem );
505 };
506
507 void( entity pParent, entity pItem ) Menu_InheritRunFlag =
508 {
509         // reset the runflag
510         pItem._runFlag = pItem._runFlag & (RUNFLAG_MOUSEINAREA | RUNFLAG_DELETEFRAME | RUNFLAG_DELETETOGGLE | RUNFLAG_SPAWNED);
511         // inherit template
512         if( pParent._runFlag & RUNFLAG_TEMPLATE )
513                 pItem._runFlag = pItem._runFlag | RUNFLAG_TEMPLATE;
514         // inherit drawonly
515         if( ( pParent._runFlag & RUNFLAG_CHILDDRAWONLY ) ||
516             ( pParent.flag & FLAG_CHILDDRAWONLY ) )
517                 pItem._runFlag = pItem._runFlag | RUNFLAG_CHILDDRAWONLY;
518         // inherit drawupdateonly
519         if( ( pParent._runFlag & RUNFLAG_CHILDDRAWUPDATEONLY )  ||
520             ( pParent.flag & FLAG_CHILDDRAWUPDATEONLY ) )
521                 pItem._runFlag = pItem._runFlag | RUNFLAG_CHILDDRAWUPDATEONLY;
522         if( pParent._runFlag & RUNFLAG_HIDDEN )
523                 pItem._runFlag = pItem._runFlag | RUNFLAG_HIDDEN;
524 };
525
526 void() Menu_UpdateRunFlags =
527 {
528         local vector lPos, lSize, lOrg;
529
530         lPos = Menu_Clip_Position;
531         lSize = Menu_Clip_Size;
532         lOrg = Menu_Origin;
533
534         Menu_Process_Setup();
535
536         // set the runflag of the active window
537         Menu_ActiveWindow._runFlag = Menu_ActiveWindow._runFlag & RUNFLAG_MOUSEINAREA;
538
539         // update runflag by using the view tree
540         Menu_ProcessRunFlag( Menu_ActiveWindow );
541
542         Menu_Clip_Size = lSize;
543         Menu_Clip_Position = lPos;
544         Menu_Origin = lOrg;
545         Menu_Cursor_Position = Cursor_Position - Menu_Origin;
546 };
547
548 bool( entity pEntity ) Menu_HasEvents =
549 {
550         if( pEntity._runFlag & RUNFLAG_CHILDDRAWONLY )
551                 return false;
552         if( pEntity._runFlag & RUNFLAG_CHILDDRAWUPDATEONLY )
553                 return false;
554         if( pEntity.flag & FLAG_DRAWONLY )
555                 return false;
556         if( pEntity.flag & FLAG_DRAWUPDATEONLY )
557                 return false;
558         //if( pEntity._runflag & RUNFLAG_HIDDEN )
559         //      return false;
560         return true;
561 };
562
563 #ifdef USEFUNCTIONS
564 bool( entity pEntity ) Menu_IsVisible =
565 {
566         return !(pEntity._runFlag & (RUNFLAG_HIDDEN | RUNFLAG_CLIPPED));
567 };
568
569 bool( entity pEntity ) Menu_IsSelectable =
570 {
571         return !(pEntity._runFlag & RUNFLAG_NOSELECT); // && !(pEntity._runFlag & RUNFLAG_TEMPLATE);
572 };
573
574 bool( entity pEntity ) Menu_IsTemplate =
575 {
576         return pEntity._runFlag & RUNFLAG_TEMPLATE;
577 };
578
579 bool( entity pEntity ) Menu_IsEmbedded =
580 {
581         return pEntity.flag & FLAG_EMBEDDED;
582 };
583
584 string( entity pItem ) Menu_GetName =
585 {
586         return substring( pItem.name, strlen( pItem.parent ) + 2, 100000 );
587 };
588 #endif