]> Shamusworld >> Repos - apple2/blob - src/gui/menu.cpp
Moved stuff into trunk (part 2)...
[apple2] / src / gui / menu.cpp
1 //
2 // MENU.CPP
3 //
4 // Graphical User Interface menu support
5 // by James L. Hammons
6 //
7 // JLH = James L. Hammons <jlhamm@acm.org>
8 //
9 // WHO  WHEN        WHAT
10 // ---  ----------  ------------------------------------------------------------
11 // JLH  02/09/2006  Created this file
12 // JLH  02/13/2006  Added rendering support
13 //
14
15 #include "menu.h"
16 #include "guimisc.h"
17
18 //#define DEBUG_MENU
19
20 #ifdef DEBUG_MENU
21 #include "log.h"
22 #endif
23
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
29 #else
30 #define MASK_R 0x000000FF
31 #define MASK_G 0x0000FF00
32 #define MASK_B 0x00FF0000
33 #define MASK_A 0xFF000000
34 #endif
35
36 //
37 // MenuItems class implementation
38 //
39
40 MenuItems::MenuItems(): charLength(0), popupBackstore(NULL)
41 {
42 }
43
44 bool MenuItems::Inside(uint32 x, uint32 y)
45 {
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);
48 }
49
50 //
51 // Menu class implementation
52 //
53
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)
64 {
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;
70 }
71
72 Menu::~Menu()
73 {
74         for(uint32 i=0; i<itemList.size(); i++)
75         {
76                 if (itemList[i].popupBackstore)
77                         SDL_FreeSurface(itemList[i].popupBackstore);
78         }
79 }
80
81 void Menu::HandleKey(SDLKey key)
82 {
83         SaveStateVariables();
84
85         for(uint32 i=0; i<itemList.size(); i++)
86         {
87                 for(uint32 j=0; j<itemList[i].item.size(); j++)
88                 {
89                         if (itemList[i].item[j].hotKey == key)
90                         {
91                                 SDL_Event event;
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);
96
97                                 clicked = false, menuChosen = menuItemChosen = -1;
98                                 break;
99                         }
100                 }
101         }
102
103         CheckStateAndRedrawIfNeeded();
104 }
105
106 void Menu::HandleMouseMove(uint32 x, uint32 y)
107 {
108 #ifdef DEBUG_MENU
109 WriteLog("--> Inside Menu::HandleMouseMove()...\n");
110 #endif
111         SaveStateVariables();
112
113         inside = insidePopup = 0;
114
115         if (Inside(x, y))
116         {
117                 // Find out *where* we are inside the menu bar
118                 uint32 xpos = extents.x;
119
120                 for(uint32 i=0; i<itemList.size(); i++)
121                 {
122                         uint32 width = (itemList[i].title.length() + 2) * GetFontWidth();
123
124                         if (x >= xpos && x < xpos + width)
125                         {
126                                 inside = i + 1;
127                                 menuChosen = i;
128                                 break;
129                         }
130
131                         xpos += width;
132                 }
133         }
134
135         if (!Inside(x, y) && !clicked)
136         {
137                 menuChosen = -1;
138         }
139
140         if (itemList[menuChosen].Inside(x, y) && clicked)
141         {
142                 insidePopup = ((y - itemList[menuChosen].extents.y) / GetFontHeight()) + 1;
143                 menuItemChosen = insidePopup - 1;
144         }
145
146         CheckStateAndRedrawIfNeeded();
147 }
148
149 void Menu::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
150 {
151 #ifdef DEBUG_MENU
152 WriteLog("--> Inside Menu::HandleMouseButton()...\n");
153 #endif
154         SaveStateVariables();
155
156         if (!clicked)
157         {
158                 if (mouseDown)
159                 {
160                         if (inside)
161                                 clicked = true;
162                         else
163                                 menuChosen = -1;                                // clicked is already false...!
164                 }
165         }
166         else                                                                            // clicked == true
167         {
168                 if (insidePopup && !mouseDown)                  // I.e., mouse-button-up
169                 {
170                         activated = true;
171
172                         if (itemList[menuChosen].item[menuItemChosen].action != NULL)
173                         {
174                                 SDL_Event event;
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);
179                         }
180
181                         clicked = false, menuChosen = menuItemChosen = -1;
182                 }
183
184                 if (!inside && !insidePopup && mouseDown)
185                         clicked = false, menuChosen = menuItemChosen = -1;
186         }
187
188         CheckStateAndRedrawIfNeeded();
189 }
190
191 void Menu::Draw(void)
192 {
193 #ifdef DEBUG_MENU
194 WriteLog("--> Inside Menu::Draw()...\n");
195 #endif
196         char separator[] = "--------------------------------------------------------";
197
198         uint32 xpos = extents.x;
199
200         for(uint32 i=0; i<itemList.size(); i++)
201         {
202                 uint32 color1 = fgColor, color2 = bgColor;
203
204                 if (inside == (i + 1) || (menuChosen != -1 && (uint32)menuChosen == i))
205                         color1 = fgColorHL, color2 = bgColorHL;
206
207                 DrawStringOpaque(screen, xpos, extents.y, color1, color2,
208                         " %s ", itemList[i].title.c_str());
209                 xpos += (itemList[i].title.length() + 2) * GetFontWidth();
210         }
211
212         // Prime the backstore if we're about to draw a popup...
213         if (!clickedSave && clicked)                            // If we transitioned from no popup to popup
214 #ifdef DEBUG_MENU
215         {
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);
222 #endif
223                 SDL_BlitSurface(screen, &itemList[menuChosen].extents, itemList[menuChosen].popupBackstore, NULL);
224 #ifdef DEBUG_MENU
225         }
226 #endif
227
228         // Draw sub menu (but only if active)
229         if (clicked)
230         {
231                 uint32 ypos = extents.y + GetFontHeight() + 1;
232
233                 for(uint32 i=0; i<itemList[menuChosen].item.size(); i++)
234                 {
235                         uint32 color1 = fgColor, color2 = bgColor;
236
237                         if (insidePopup == i + 1)
238                                 color1 = fgColorHL, color2 = bgColorHL, menuItemChosen = i;
239
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());
244                         else
245                                 DrawStringOpaque(screen, itemList[menuChosen].extents.x, ypos,
246                                         fgColor, bgColor, "%.*s", itemList[menuChosen].charLength + 2, separator);
247
248                         ypos += GetFontHeight();
249                 }
250         }
251
252         // Do cleanup if we're done with the popup menu
253         if (clickedSave && !clicked)                            // If we transitioned from popup to no popup
254         {
255                 SDL_Rect r;
256
257                 r.x = itemList[menuChosenSave].extents.x;
258                 r.y = itemList[menuChosenSave].extents.y;
259                 SDL_BlitSurface(itemList[menuChosenSave].popupBackstore, NULL, screen, &r);
260         }
261
262         needToRefreshScreen = true;
263 }
264
265 void Menu::Notify(Element *)
266 {
267 }
268
269 void Menu::Add(MenuItems mi)
270 {
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();
274
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();
280
281         mi.popupBackstore = SDL_CreateRGBSurface(SDL_SWSURFACE, mi.extents.w, mi.extents.h, 32,
282                 MASK_R, MASK_G, MASK_B, 0x00);
283
284         itemList.push_back(mi);
285         extents.w += (mi.title.length() + 2) * GetFontWidth();
286
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);
289 #ifdef DEBUG_MENU
290 WriteLog("--> Added menu item...\n    pubs x/y/w/h = %u/%u/%u/%u\n    surface = %08X\n",
291         mi.extents.x,
292         mi.extents.y,
293         mi.popupBackstore->w,
294         mi.popupBackstore->h,
295         mi.popupBackstore);
296 #endif
297 }
298
299 void Menu::SaveStateVariables(void)
300 {
301         activatedSave = activated;
302         clickedSave = clicked;
303         insideSave = inside;
304         insidePopupSave = insidePopup;
305         menuChosenSave = menuChosen;
306         menuItemChosenSave = menuItemChosen;
307 }
308
309 void Menu::CheckStateAndRedrawIfNeeded(void)
310 {
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)
315                 Draw();
316 }