4 // Graphical User Interface menu support
7 // JLH = James L. Hammons <jlhamm@acm.org>
10 // --- ---------- ------------------------------------------------------------
11 // JLH 02/09/2006 Created this file
12 // JLH 02/13/2006 Added rendering support
24 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
25 #define MASK_R 0xFF000000
26 #define MASK_G 0x00FF0000
27 #define MASK_B 0x0000FF00
28 #define MASK_A 0x000000FF
30 #define MASK_R 0x000000FF
31 #define MASK_G 0x0000FF00
32 #define MASK_B 0x00FF0000
33 #define MASK_A 0xFF000000
37 // MenuItems class implementation
40 MenuItems::MenuItems(): charLength(0), popupBackstore(NULL)
44 bool MenuItems::Inside(uint32 x, uint32 y)
46 return (x >= (uint32)extents.x && x < (uint32)(extents.x + extents.w)
47 && y >= (uint32)extents.y && y < (uint32)(extents.y + extents.h) ? true : false);
51 // Menu class implementation
54 Menu::Menu(uint32 x/*= 0*/, uint32 y/*= 0*/, uint32 w/*= 0*/, uint32 h/*= 0*/,
55 uint8 fgcR/*= 0x00*/, uint8 fgcG/*= 0x00*/, uint8 fgcB/*= 0x7F*/, uint8 fgcA/*= 0xFF*/,
56 uint8 bgcR/*= 0x3F*/, uint8 bgcG/*= 0x3F*/, uint8 bgcB/*= 0xFF*/, uint8 bgcA/*= 0xFF*/,
57 uint8 fgchR/*= 0x3F*/, uint8 fgchG/*= 0x3F*/, uint8 fgchB/*= 0xFF*/, uint8 fgchA/*= 0xFF*/,
58 uint8 bgchR/*= 0x87*/, uint8 bgchG/*= 0x87*/, uint8 bgchB/*= 0xFF*/, uint8 bgchA/*= 0xFF*/):
59 Element(x, y, w, GetFontHeight(), fgcR, fgcG, fgcB, fgcA, bgcR, bgcG, bgcB, bgcA),
60 activated(false), clicked(false),
61 inside(0), insidePopup(0), menuChosen(-1), menuItemChosen(-1),
62 activatedSave(false), clickedSave(false),
63 insideSave(0), insidePopupSave(0), menuChosenSave(-1), menuItemChosenSave(-1)
65 // This *should* allow us to store our colors in an endian safe way... :-/
66 uint8 * c = (uint8 *)&fgColorHL;
67 c[0] = fgchR, c[1] = fgchG, c[2] = fgchB, c[3] = fgchA;
68 c = (uint8 *)&bgColorHL;
69 c[0] = bgchR, c[1] = bgchG, c[2] = bgchB, c[3] = bgchA;
74 for(uint32 i=0; i<itemList.size(); i++)
76 if (itemList[i].popupBackstore)
77 SDL_FreeSurface(itemList[i].popupBackstore);
81 void Menu::HandleKey(SDLKey key)
85 for(uint32 i=0; i<itemList.size(); i++)
87 for(uint32 j=0; j<itemList[i].item.size(); j++)
89 if (itemList[i].item[j].hotKey == key)
92 event.type = SDL_USEREVENT;
93 event.user.code = MENU_ITEM_CHOSEN;
94 event.user.data1 = (void *)itemList[i].item[j].action;
95 SDL_PushEvent(&event);
97 clicked = false, menuChosen = menuItemChosen = -1;
103 CheckStateAndRedrawIfNeeded();
106 void Menu::HandleMouseMove(uint32 x, uint32 y)
109 WriteLog("--> Inside Menu::HandleMouseMove()...\n");
111 SaveStateVariables();
113 inside = insidePopup = 0;
117 // Find out *where* we are inside the menu bar
118 uint32 xpos = extents.x;
120 for(uint32 i=0; i<itemList.size(); i++)
122 uint32 width = (itemList[i].title.length() + 2) * GetFontWidth();
124 if (x >= xpos && x < xpos + width)
135 if (!Inside(x, y) && !clicked)
140 if (itemList[menuChosen].Inside(x, y) && clicked)
142 insidePopup = ((y - itemList[menuChosen].extents.y) / GetFontHeight()) + 1;
143 menuItemChosen = insidePopup - 1;
146 CheckStateAndRedrawIfNeeded();
149 void Menu::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
152 WriteLog("--> Inside Menu::HandleMouseButton()...\n");
154 SaveStateVariables();
163 menuChosen = -1; // clicked is already false...!
166 else // clicked == true
168 if (insidePopup && !mouseDown) // I.e., mouse-button-up
172 if (itemList[menuChosen].item[menuItemChosen].action != NULL)
175 event.type = SDL_USEREVENT;
176 event.user.code = MENU_ITEM_CHOSEN;
177 event.user.data1 = (void *)itemList[menuChosen].item[menuItemChosen].action;
178 SDL_PushEvent(&event);
181 clicked = false, menuChosen = menuItemChosen = -1;
184 if (!inside && !insidePopup && mouseDown)
185 clicked = false, menuChosen = menuItemChosen = -1;
188 CheckStateAndRedrawIfNeeded();
191 void Menu::Draw(void)
194 WriteLog("--> Inside Menu::Draw()...\n");
196 char separator[] = "--------------------------------------------------------";
198 uint32 xpos = extents.x;
200 for(uint32 i=0; i<itemList.size(); i++)
202 uint32 color1 = fgColor, color2 = bgColor;
204 if (inside == (i + 1) || (menuChosen != -1 && (uint32)menuChosen == i))
205 color1 = fgColorHL, color2 = bgColorHL;
207 DrawStringOpaque(screen, xpos, extents.y, color1, color2,
208 " %s ", itemList[i].title.c_str());
209 xpos += (itemList[i].title.length() + 2) * GetFontWidth();
212 // Prime the backstore if we're about to draw a popup...
213 if (!clickedSave && clicked) // If we transitioned from no popup to popup
216 WriteLog("--> Attempting to prime pubs...\n pubs x/y/w/h = %u/%u/%u/%u\n surface = %08X\n",
217 itemList[menuChosen].extents.x,
218 itemList[menuChosen].extents.y,
219 itemList[menuChosen].extents.w,
220 itemList[menuChosen].extents.h,
221 itemList[menuChosen].popupBackstore);
223 SDL_BlitSurface(screen, &itemList[menuChosen].extents, itemList[menuChosen].popupBackstore, NULL);
228 // Draw sub menu (but only if active)
231 uint32 ypos = extents.y + GetFontHeight() + 1;
233 for(uint32 i=0; i<itemList[menuChosen].item.size(); i++)
235 uint32 color1 = fgColor, color2 = bgColor;
237 if (insidePopup == i + 1)
238 color1 = fgColorHL, color2 = bgColorHL, menuItemChosen = i;
240 if (itemList[menuChosen].item[i].name.length() > 0)
241 DrawStringOpaque(screen, itemList[menuChosen].extents.x, ypos,
242 color1, color2, " %-*.*s ", itemList[menuChosen].charLength,
243 itemList[menuChosen].charLength, itemList[menuChosen].item[i].name.c_str());
245 DrawStringOpaque(screen, itemList[menuChosen].extents.x, ypos,
246 fgColor, bgColor, "%.*s", itemList[menuChosen].charLength + 2, separator);
248 ypos += GetFontHeight();
252 // Do cleanup if we're done with the popup menu
253 if (clickedSave && !clicked) // If we transitioned from popup to no popup
257 r.x = itemList[menuChosenSave].extents.x;
258 r.y = itemList[menuChosenSave].extents.y;
259 SDL_BlitSurface(itemList[menuChosenSave].popupBackstore, NULL, screen, &r);
262 needToRefreshScreen = true;
265 void Menu::Notify(Element *)
269 void Menu::Add(MenuItems mi)
271 for(uint32 i=0; i<mi.item.size(); i++)
272 if (mi.item[i].name.length() > mi.charLength)
273 mi.charLength = mi.item[i].name.length();
275 // Set extents here as well...
276 mi.extents.x = extents.x + extents.w;
277 mi.extents.y = extents.y + GetFontHeight() + 1;
278 mi.extents.w = (mi.charLength + 2) * GetFontWidth();
279 mi.extents.h = mi.item.size() * GetFontHeight();
281 mi.popupBackstore = SDL_CreateRGBSurface(SDL_SWSURFACE, mi.extents.w, mi.extents.h, 32,
282 MASK_R, MASK_G, MASK_B, 0x00);
284 itemList.push_back(mi);
285 extents.w += (mi.title.length() + 2) * GetFontWidth();
287 //This is incorrect--this should be sampled just *before* we draw the popup! !!! FIX !!! [DONE]
288 // SDL_BlitSurface(screen, &mi.extents, mi.popupBackstore, NULL);
290 WriteLog("--> Added menu item...\n pubs x/y/w/h = %u/%u/%u/%u\n surface = %08X\n",
293 mi.popupBackstore->w,
294 mi.popupBackstore->h,
299 void Menu::SaveStateVariables(void)
301 activatedSave = activated;
302 clickedSave = clicked;
304 insidePopupSave = insidePopup;
305 menuChosenSave = menuChosen;
306 menuItemChosenSave = menuItemChosen;
309 void Menu::CheckStateAndRedrawIfNeeded(void)
311 // Check to see if any of our state variables changed since we last saved them...
312 if (activated != activatedSave || clicked != clickedSave
313 || inside != insideSave || insidePopup != insidePopupSave
314 || menuChosen != menuChosenSave || menuItemChosen != menuItemChosenSave)