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_t x, uint32_t y)
46 return (x >= (uint32_t)extents.x && x < (uint32_t)(extents.x + extents.w)
47 && y >= (uint32_t)extents.y && y < (uint32_t)(extents.y + extents.h) ? true : false);
51 // Menu class implementation
54 Menu::Menu(uint32_t x/*= 0*/, uint32_t y/*= 0*/, uint32_t w/*= 0*/, uint32_t h/*= 0*/,
55 uint8_t fgcR/*= 0x00*/, uint8_t fgcG/*= 0x00*/, uint8_t fgcB/*= 0x7F*/, uint8_t fgcA/*= 0xFF*/,
56 uint8_t bgcR/*= 0x3F*/, uint8_t bgcG/*= 0x3F*/, uint8_t bgcB/*= 0xFF*/, uint8_t bgcA/*= 0xFF*/,
57 uint8_t fgchR/*= 0x3F*/, uint8_t fgchG/*= 0x3F*/, uint8_t fgchB/*= 0xFF*/, uint8_t fgchA/*= 0xFF*/,
58 uint8_t bgchR/*= 0x87*/, uint8_t bgchG/*= 0x87*/, uint8_t bgchB/*= 0xFF*/, uint8_t 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)
66 // This *should* allow us to store our colors in an endian safe way... :-/
67 // Nope. Only on SW surfaces. With HW, all bets are off. :-(
68 uint8_t * c = (uint8_t *)&fgColorHL;
69 c[0] = fgchR, c[1] = fgchG, c[2] = fgchB, c[3] = fgchA;
70 c = (uint8_t *)&bgColorHL;
71 c[0] = bgchR, c[1] = bgchG, c[2] = bgchB, c[3] = bgchA;
73 fgColorHL = SDL_MapRGBA(screen->format, fgchR, fgchG, fgchB, fgchA);
74 bgColorHL = SDL_MapRGBA(screen->format, bgchR, bgchG, bgchB, bgchA);
80 for(uint32_t i=0; i<itemList.size(); i++)
82 if (itemList[i].popupBackstore)
83 SDL_FreeSurface(itemList[i].popupBackstore);
87 void Menu::HandleKey(SDL_Scancode key)
91 for(uint32_t i=0; i<itemList.size(); i++)
93 for(uint32_t j=0; j<itemList[i].item.size(); j++)
95 if (itemList[i].item[j].hotKey == key)
98 event.type = SDL_USEREVENT;
99 event.user.code = MENU_ITEM_CHOSEN;
100 event.user.data1 = (void *)itemList[i].item[j].action;
101 SDL_PushEvent(&event);
103 clicked = false, menuChosen = menuItemChosen = -1;
109 CheckStateAndRedrawIfNeeded();
112 void Menu::HandleMouseMove(uint32_t x, uint32_t y)
115 WriteLog("--> Inside Menu::HandleMouseMove()...\n");
117 SaveStateVariables();
119 inside = insidePopup = 0;
123 // Find out *where* we are inside the menu bar
124 uint32_t xpos = extents.x;
126 for(uint32_t i=0; i<itemList.size(); i++)
128 uint32_t width = (itemList[i].title.length() + 2) * GetFontWidth();
130 if (x >= xpos && x < xpos + width)
141 if (!Inside(x, y) && !clicked)
146 if (itemList[menuChosen].Inside(x, y) && clicked)
148 insidePopup = ((y - itemList[menuChosen].extents.y) / GetFontHeight()) + 1;
149 menuItemChosen = insidePopup - 1;
152 CheckStateAndRedrawIfNeeded();
155 void Menu::HandleMouseButton(uint32_t x, uint32_t y, bool mouseDown)
158 WriteLog("--> Inside Menu::HandleMouseButton()...\n");
160 SaveStateVariables();
169 menuChosen = -1; // clicked is already false...!
172 else // clicked == true
174 if (insidePopup && !mouseDown) // I.e., mouse-button-up
178 if (itemList[menuChosen].item[menuItemChosen].action != NULL)
181 event.type = SDL_USEREVENT;
182 event.user.code = MENU_ITEM_CHOSEN;
183 event.user.data1 = (void *)itemList[menuChosen].item[menuItemChosen].action;
184 SDL_PushEvent(&event);
187 clicked = false, menuChosen = menuItemChosen = -1;
190 if (!inside && !insidePopup && mouseDown)
191 clicked = false, menuChosen = menuItemChosen = -1;
194 CheckStateAndRedrawIfNeeded();
197 void Menu::Draw(void)
200 WriteLog("--> Inside Menu::Draw()...\n");
202 char separator[] = "--------------------------------------------------------";
204 uint32_t xpos = extents.x;
206 for(uint32_t i=0; i<itemList.size(); i++)
208 uint32_t color1 = fgColor, color2 = bgColor;
210 if (inside == (i + 1) || (menuChosen != -1 && (uint32_t)menuChosen == i))
211 color1 = fgColorHL, color2 = bgColorHL;
213 DrawStringOpaque(screen, xpos, extents.y, color1, color2,
214 " %s ", itemList[i].title.c_str());
215 xpos += (itemList[i].title.length() + 2) * GetFontWidth();
218 // Prime the backstore if we're about to draw a popup...
219 if (!clickedSave && clicked) // If we transitioned from no popup to popup
222 WriteLog("--> Attempting to prime pubs...\n pubs x/y/w/h = %u/%u/%u/%u\n surface = %08X\n",
223 itemList[menuChosen].extents.x,
224 itemList[menuChosen].extents.y,
225 itemList[menuChosen].extents.w,
226 itemList[menuChosen].extents.h,
227 itemList[menuChosen].popupBackstore);
229 SDL_BlitSurface(screen, &itemList[menuChosen].extents, itemList[menuChosen].popupBackstore, NULL);
234 // Draw sub menu (but only if active)
237 uint32_t ypos = extents.y + GetFontHeight() + 1;
239 for(uint32_t i=0; i<itemList[menuChosen].item.size(); i++)
241 uint32_t color1 = fgColor, color2 = bgColor;
243 if (insidePopup == i + 1)
244 color1 = fgColorHL, color2 = bgColorHL, menuItemChosen = i;
246 if (itemList[menuChosen].item[i].name.length() > 0)
247 DrawStringOpaque(screen, itemList[menuChosen].extents.x, ypos,
248 color1, color2, " %-*.*s ", itemList[menuChosen].charLength,
249 itemList[menuChosen].charLength, itemList[menuChosen].item[i].name.c_str());
251 DrawStringOpaque(screen, itemList[menuChosen].extents.x, ypos,
252 fgColor, bgColor, "%.*s", itemList[menuChosen].charLength + 2, separator);
254 ypos += GetFontHeight();
258 // Do cleanup if we're done with the popup menu
259 if (clickedSave && !clicked) // If we transitioned from popup to no popup
263 r.x = itemList[menuChosenSave].extents.x;
264 r.y = itemList[menuChosenSave].extents.y;
265 SDL_BlitSurface(itemList[menuChosenSave].popupBackstore, NULL, screen, &r);
268 needToRefreshScreen = true;
271 void Menu::Notify(Element *)
275 void Menu::Add(MenuItems mi)
277 for(uint32_t i=0; i<mi.item.size(); i++)
278 if (mi.item[i].name.length() > mi.charLength)
279 mi.charLength = mi.item[i].name.length();
281 // Set extents here as well...
282 mi.extents.x = extents.x + extents.w;
283 mi.extents.y = extents.y + GetFontHeight() + 1;
284 mi.extents.w = (mi.charLength + 2) * GetFontWidth();
285 mi.extents.h = mi.item.size() * GetFontHeight();
287 mi.popupBackstore = SDL_CreateRGBSurface(SDL_SWSURFACE, mi.extents.w, mi.extents.h, 32,
288 MASK_R, MASK_G, MASK_B, 0x00);
290 itemList.push_back(mi);
291 extents.w += (mi.title.length() + 2) * GetFontWidth();
293 //This is incorrect--this should be sampled just *before* we draw the popup! !!! FIX !!! [DONE]
294 // SDL_BlitSurface(screen, &mi.extents, mi.popupBackstore, NULL);
296 WriteLog("--> Added menu item...\n pubs x/y/w/h = %u/%u/%u/%u\n surface = %08X\n",
299 mi.popupBackstore->w,
300 mi.popupBackstore->h,
305 void Menu::SaveStateVariables(void)
307 activatedSave = activated;
308 clickedSave = clicked;
310 insidePopupSave = insidePopup;
311 menuChosenSave = menuChosen;
312 menuItemChosenSave = menuItemChosen;
315 void Menu::CheckStateAndRedrawIfNeeded(void)
317 // Check to see if any of our state variables changed since we last saved them...
318 if (activated != activatedSave || clicked != clickedSave
319 || inside != insideSave || insidePopup != insidePopupSave
320 || menuChosen != menuChosenSave || menuItemChosen != menuItemChosenSave)