menu.c

Go to the documentation of this file.
00001 
00025 #include "menu.h"
00026 
00027 #ifdef WIN32
00028 // Avoid a name conflict with a function in the win32 API
00029 #undef  RemoveMenu
00030 #define RemoveMenu  _RemoveMenu_win32_
00031 #endif
00032 
00033 #include "genconmac.h"
00034 #include "color.h"
00035 #include "states.h"
00036 #include "message.h"
00037 #include "net.h"
00038 #include <stdlib.h>
00039 #include <assert.h>
00040 
00041 #ifdef WIN32
00042 // Avoid multiply defined symbols
00043 #undef  RemoveMenu
00044 #define RemoveMenu _RemoveMenu_tank_
00045 #endif
00046 
00047 
00051 #define MENU_MARGIN  96
00052 
00056 #define MARKER_THICKNESS  3
00057 
00061 #define MARKER_LENGTH  24
00062 
00063 extern RenderItem background;
00064 
00070 static MenuItem *items;
00071 
00077 static int focus;
00078 
00084 static int numItems;
00085 
00090 static RenderItem *markers;
00091 
00092 static void PlaceMarkers() {
00093     int loop;
00094     RenderItem *mark;
00095     for (mark = markers, loop = 8; loop > 0; mark++, loop--) {
00096         // on the left
00097         if (loop & 2) {
00098             mark->loc.x = items[focus].region.x - MARKER_THICKNESS;
00099         }
00100         // on the right
00101         else {
00102             mark->loc.x = items[focus].region.x + items[focus].region.w
00103             + MARKER_THICKNESS - mark->loc.w;
00104         }
00105         // on the top
00106         if (loop & 4) {
00107             mark->loc.y = items[focus].region.y - MARKER_THICKNESS;
00108         }
00109         // on the bottom
00110         else {
00111             mark->loc.y = items[focus].region.y + items[focus].region.h
00112             + MARKER_THICKNESS - mark->loc.h;
00113         }
00114         // make the mark visible
00115         PlaceRenderItem(mark);
00116     }
00117 }
00118 
00127 static Bool ResizeMenu(int winW, int winH) {
00128     MenuItem *item;
00129     int loop;
00130     int w, h = MARKER_THICKNESS;  // stores the proposed menu size
00131     int y;         // the y coordinate of the last placed item
00132     int x;         // the new x coordinate for all items
00133     // propose value for w
00134     if ((w = winW - MENU_MARGIN * 2) < (MENU_MARGIN * 2)) {
00135         // inadequate size -- do not resize
00136         return FALSE;
00137     }
00138     // loop to find good values for the width and height before modifying
00139     // the menu items
00140     for (item = items, loop = numItems; loop > 0; item++, loop--) {
00141         // all menu items must have an update function
00142         assert(item->update != NULL);
00143         // all menu items must have their minimum sizes set
00144         assert((item->minWidth > 0) && (item->minHeight > 0));
00145         // check for inadequate width
00146         if (item->minWidth > (winW - MARKER_THICKNESS * 2)) {
00147             // use maximum width
00148             w = winW - MARKER_THICKNESS * 2;
00149         }
00150         // check for a need for more width
00151         if (item->minWidth > w) {
00152             // record the greater width
00153             w = item->minWidth;
00154         }
00155         // accumulate the height
00156         h += item->minHeight + MARKER_THICKNESS;
00157     }
00158     // compute the x coordinate
00159     x = (winW - w) >> 1;
00160     // check for a height that won't fit
00161     if (h > (loop = winH - TOP_MARGIN - BOTTOM_MARGIN * 2 - 
00162     textItems[TEXTITEM_TITLE].loc.h)) {
00163         // remove the title's hieght
00164         loop += textItems[TEXTITEM_TITLE].loc.h + TOP_MARGIN - BOTTOM_MARGIN;
00165         // don't show the title
00166         ClearRenderItem(&(textItems[TEXTITEM_TITLE]));
00167         // see if it will fit with the normal bottom margin
00168         if (h <= loop) {
00169             // set the height with the bottom margin up from the bottm
00170             y = winH - h - BOTTOM_MARGIN;
00171         }
00172         else if ((y = winH - (h >> 1)) < 0) {
00173             y = 0;
00174         }
00175         // no remaining height
00176         h = 0;
00177     }
00178     // the items have adequate vertical room
00179     else {
00180         // place the first item below the title
00181         y = TOP_MARGIN + BOTTOM_MARGIN + textItems[TEXTITEM_TITLE].loc.h;
00182         // compute the remaining height
00183         h = winH - y - BOTTOM_MARGIN - h;
00184         // assure the title is drawn at the correct location
00185         textItems[TEXTITEM_TITLE].loc.x =
00186         (winW >> 1) - (textItems[TEXTITEM_TITLE].loc.w >> 1);
00187         PlaceRenderItem(&(textItems[TEXTITEM_TITLE]));
00188     }
00189     // loop through the items to modify their size
00190     for (item = items, loop = numItems; loop > 0; item++, loop--) {
00191         // set x
00192         item->region.x = x;
00193         // set y
00194         item->region.y = y;
00195         // set width
00196         item->region.w = w;
00197         // set height
00198         item->region.h = item->minHeight;
00199         // check for expandable height
00200         if (item->expandHeight) {
00201             // give it the the remaining height, too
00202             item->region.h += h;
00203         }
00204         // advance the y coordinate for the next item
00205         y += item->region.h + MARKER_THICKNESS;
00206         // clear the placed flag
00207         item->isPlaced = FALSE;
00208     }
00209     // update the markers
00210     PlaceMarkers();
00211     // resize successful
00212     return TRUE;
00213 }
00214 
00215 void SetupMenu(MenuItem *menu, int first, int count) {
00216     int loop;
00217     RenderItem *mark;
00218     // assure the item that had input focus initially is within the array of
00219     // items
00220     assert((first >= 0) && (first < count));
00221     // setup the internal data
00222     items = menu;
00223     numItems = count;
00224     focus = first;
00225     // set the focus flag of the focused item
00226     items[focus].hasFocus = TRUE;
00227     // put the backgound image in place
00228     background.renderer = SolidFillRenderer;
00229     background.data = &(colorVals[COLOR_REGBACK]);
00230     background.loc.w = dInfo.pw;
00231     background.loc.h = dInfo.ph;
00232     AddRenderItem(&background, LAYER_BACKGROUND);
00233     PlaceRenderItem(&background);
00234     // place the title text in the top; it will be cenetered by resize
00235     textItems[TEXTITEM_TITLE].loc.y = TOP_MARGIN;
00236     // initalize the markers
00237     markers = calloc(8, sizeof(RenderItem));
00238     for (mark = markers, loop = 8; loop > 0; mark++, loop--) {
00239         // tall marker?
00240         if (loop & 1) {
00241             mark->loc.w = MARKER_THICKNESS;
00242             mark->loc.h = MARKER_LENGTH;
00243         }
00244         // short marker
00245         else {
00246             mark->loc.w = MARKER_LENGTH;
00247             mark->loc.h = MARKER_THICKNESS;
00248         }
00249         // set the render function
00250         mark->renderer = SolidFillRenderer;
00251         // set the color
00252         mark->data = &(colorVals[COLOR_OBSTACLE]);
00253         // put the mark in a layer
00254         AddRenderItem(mark, LAYER_OBSTACLES);
00255     }
00256     // size the menu items
00257     ResizeMenu(dInfo.pw, dInfo.ph);
00258     // get key Unicode values (slightly broken -- assumes all chars are 2 bytes)
00259     SDL_EnableUNICODE(TRUE);
00260 }
00261 
00262 void RemoveMenu() {
00263     int loop;
00264     // get rid of the markers
00265     for (loop = 0; loop < 8; loop++) {
00266         RemoveRenderItem(&(markers[loop]));
00267     }
00268     // free up the memory for the markers
00269     free(markers);
00270     RemoveRenderItem(&background);
00271     // no longer need the key values, so avoid the overhead
00272     SDL_EnableUNICODE(FALSE);
00273 }
00274 
00275 int MenuRun() {
00276     int loop, state = 0;
00277     MenuItem *item;
00278     SDL_Event event;
00279     rtsa_char keyval = 0;
00280     
00281     // clear the event storing a typed character
00282     eventState[EC_FORWARD] = eventState[EC_BACKWARD] = 0;
00283     // loop through all waiting events
00284     while (SDL_PollEvent(&event)) {
00285         // figure event type
00286         switch (event.type) {
00287             case SDL_QUIT:
00288                 return STATE_QUIT;
00289                 break;
00290             case SDL_ACTIVEEVENT:
00291                 // some strange focus thing
00292                 /*
00293                 if (event.active.gain == 0)
00294                     eventState[EC_INACTIVE] = TRUE;
00295                 else
00296                     eventState[EC_INACTIVE] = FALSE;
00297                 */
00298                 break;
00299             case SDL_KEYDOWN:
00300                 // handle key presses
00301                 switch (event.key.keysym.sym) {
00302                     case SDLK_ESCAPE:
00303                         return STATE_QUIT;
00304                         break;
00305                     case SDLK_UP:
00306                         eventState[EC_FORWARD] |= 1;
00307                         break;
00308                     case SDLK_DOWN:
00309                         eventState[EC_BACKWARD] |= 1;
00310                         break;
00311                     case SDLK_TAB:
00312                         if (event.key.keysym.mod & KMOD_SHIFT) {
00313                             eventState[EC_FORWARD] |= 1;
00314                         }
00315                         else {
00316                             eventState[EC_BACKWARD] |= 1;
00317                         }
00318                         break;
00319                     case SDLK_LEFT:
00320                         eventState[EC_ROTLEFT] |= 1;
00321                         break;
00322                     case SDLK_RIGHT:
00323                         eventState[EC_ROTRIGHT] |= 1;
00324                         break;
00325                     case SDLK_SPACE:
00326                         keyval = ' ';
00327                         eventState[EC_CHARACTER] |= 1;
00328                     case SDLK_RETURN:
00329                         eventState[EC_FIRE] |= 1;
00330                         break;
00331                     default:
00332                         #ifdef USE_GETTEXT
00333                         eventState[EC_CHARACTER] |= 1;
00334                         // store the key code
00335                         keyval = event.key.keysym.unicode;
00336                         // modify for upper/lower case
00337                         /*  was once useful, but I *think* it isn't needed now
00338                         if (event.key.keysym.mod & KMOD_SHIFT) {
00339                             eventState[EC_CHARACTER] -= 'a' - 'A';
00340                         }
00341                         */
00342                         #else
00343                         // only accept keys that fit within ASCII
00344                         if ((event.key.keysym.unicode & 0xFF80) == 0) {
00345                             eventState[EC_CHARACTER] |= 1;
00346                             keyval = (rtsa_char)event.key.keysym.unicode;
00347                         }
00348                         #endif
00349                         break;
00350                 }
00351                 break;
00352             case SDL_KEYUP:
00353                 switch (event.key.keysym.sym) {
00354                     case SDLK_LEFT:
00355                         eventState[EC_ROTLEFT] = 0;
00356                         break;
00357                     case SDLK_RIGHT:
00358                         eventState[EC_ROTRIGHT] = 0;
00359                         break;
00360                     case SDLK_SPACE:
00361                         eventState[EC_CHARACTER] = 0;
00362                     case SDLK_RETURN:
00363                         eventState[EC_FIRE] = 0;
00364                         break;
00365                     case SDLK_BACKSPACE:
00366                         return STATE_SPLASH;
00367                     default:
00368                         if (event.key.keysym.unicode) {
00369                             eventState[EC_CHARACTER] = 0;
00370                         }
00371                         break;
00372                 }
00373                 break;
00374             case SDL_JOYAXISMOTION:
00375                 if (!(SDL_GetAppState() & SDL_APPINPUTFOCUS)) break;
00376                 // x-axis
00377                 if (event.jaxis.axis == 0) {
00378                     // right
00379                     if (event.jaxis.value > 16384) {
00380                         eventState[EC_ROTRIGHT] |= 1;
00381                         eventState[EC_ROTLEFT] = 0;
00382                     }
00383                     // left
00384                     else if (event.jaxis.value < -16384) {
00385                         eventState[EC_ROTLEFT] |= 1;
00386                         eventState[EC_ROTRIGHT] = 0;
00387                     }
00388                     // center
00389                     else {
00390                         eventState[EC_ROTLEFT] = 0;
00391                         eventState[EC_ROTRIGHT] = 0;
00392                     }
00393                 }
00394                 // y-axis
00395                 else if (event.jaxis.axis == 1) {
00396                     // down
00397                     if (event.jaxis.value > 16384) {
00398                         eventState[EC_BACKWARD] |= 1;
00399                         eventState[EC_FORWARD] = 0;
00400                     }
00401                     // up
00402                     else if (event.jaxis.value < -16384) {
00403                         eventState[EC_FORWARD] |= 1;
00404                         eventState[EC_BACKWARD] = 0;
00405                     }
00406                     // center
00407                     else {
00408                         eventState[EC_FORWARD] = 0;
00409                         eventState[EC_BACKWARD] = 0;
00410                     }
00411                 }
00412                 break;
00413             case SDL_JOYBUTTONDOWN:
00414                 if (!(SDL_GetAppState() & SDL_APPINPUTFOCUS)) break;
00415                 eventState[EC_FIRE] |= 1;
00416                 break;
00417             case SDL_JOYBUTTONUP:
00418                 if (!(SDL_GetAppState() & SDL_APPINPUTFOCUS)) break;
00419                 eventState[EC_FIRE] = 0;
00420                 break;
00421             case SDL_VIDEORESIZE:
00422                 // attempt to resize the menu
00423                 if (ResizeMenu(event.resize.w, event.resize.h)) {
00424                     // attempt to resize the window (shouldn't ever fail)
00425                     ResizeWindow(event.resize.w, event.resize.h);
00426                     // assure good color values
00427                     SetupColorVals();
00428                     // put the backgound image into its new place
00429                     background.loc.w = dInfo.pw;
00430                     background.loc.h = dInfo.ph;
00431                     PlaceRenderItem(&background);
00432                 }
00433             default:
00434                 break;
00435         }
00436     }
00437     // ----- run network code ----
00438     ProcessNetworkMessages();
00439     // see if a focus change is possible
00440     if ((numItems > 1) && !items[focus].lockFocus) {
00441         // check for the down/next item input
00442         if (eventState[EC_BACKWARD]) {
00443             // loop through the next items
00444             for (loop = focus + 1; loop < numItems; loop++) {
00445                 // check for one that can aquire the focus
00446                 if (items[loop].active) {
00447                     // the old focus is lost
00448                     items[focus].hasFocus = FALSE;
00449                     if (items[focus].focus) {
00450                         (*(items[focus].focus))(&(items[focus]));
00451                     }
00452                     // set the focus to this one
00453                     items[focus = loop].hasFocus = TRUE;
00454                     if (items[focus].focus) {
00455                         (*(items[focus].focus))(&(items[focus]));
00456                     }
00457                     // update the marker locations
00458                     PlaceMarkers();
00459                     break;
00460                 }
00461             }
00462         }
00463         // check for the up/previous item input
00464         else if (eventState[EC_FORWARD]) {
00465             // loop through the previous items
00466             for (loop = focus - 1; loop >= 0; loop--) {
00467                 // check for one that can aquire the focus
00468                 if (items[loop].active) {
00469                     // the old focus is lost
00470                     items[focus].hasFocus = FALSE;
00471                     if (items[focus].focus) {
00472                         (*(items[focus].focus))(&(items[focus]));
00473                     }
00474                     // set the focus to this one
00475                     items[focus = loop].hasFocus = TRUE;
00476                     if (items[focus].focus) {
00477                         (*(items[focus].focus))(&(items[focus]));
00478                     }
00479                     // update the marker locations
00480                     PlaceMarkers();
00481                     break;
00482                 }
00483             }
00484         }
00485     }
00486     // pass the input on to the focused item
00487     if (items[focus].input) {
00488         // run input processing and check for a state change request
00489         if ((state = (*(items[focus].input))(&(items[focus]), eventState,
00490         keyval)) != 0) {
00491             return state;
00492         }
00493     }
00494     // update all the menu items
00495     for (item = items, loop = numItems; loop > 0; item++, loop--) {
00496         // call the update
00497         if ((state = (*(item->update))(item)) != 0) {
00498             return state;
00499         }
00500     }
00501     // stay in this state
00502     return STATE_MENUROOT;
00503 }
00504 

Generated on Mon May 28 04:41:39 2007 for Retro Tank Super Attack by  doxygen 1.5.2