1 /*
2 Copyright (c) 2009-2010 Tero Lindeman (kometbomb)
3
4 Permission is hereby granted, free of charge, to any person
5 obtaining a copy of this software and associated documentation
6 files (the "Software"), to deal in the Software without
7 restriction, including without limitation the rights to use,
8 copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following
11 conditions:
12
13 The above copyright notice and this permission notice shall be
14 included in all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26 #include "menu.h"
27 #include "gui/bevel.h"
28 #include "gfx/font.h"
29 #include "gui/view.h"
30 #include "shortcuts.h"
31 #include "bevdefs.h"
32 #include <string.h>
33
34 #define SC_SIZE 64
35
36 enum { ZONE, DRAW };
37
38 static void (*menu_close_hook)(void) = NULL;
39 static const Menu *current_menu = NULL;
40 static const Menu *current_menu_action = NULL;
41 static const KeyShortcut *shortcuts = NULL;
42 static GfxSurface *menu_gfx = NULL;
43 static const Font * menu_font, *shortcut_font, *header_font;
44 static const Font * menu_font_selected, *shortcut_font_selected, *header_font_selected;
45
open_menu(const Menu * mainmenu,const Menu * action,void (* close_hook)(void),const KeyShortcut * _shortcuts,const Font * headerfont,const Font * headerfont_selected,const Font * menufont,const Font * menufont_selected,const Font * shortcutfont,const Font * shortcutfont_selected,GfxSurface * gfx)46 void open_menu(const Menu *mainmenu, const Menu *action, void (*close_hook)(void), const KeyShortcut *_shortcuts,
47 const Font *headerfont, const Font *headerfont_selected,
48 const Font *menufont, const Font *menufont_selected,
49 const Font *shortcutfont, const Font *shortcutfont_selected, GfxSurface *gfx)
50 {
51 current_menu = mainmenu;
52 current_menu_action = action;
53 menu_close_hook = close_hook;
54 shortcuts = _shortcuts;
55 menu_gfx = gfx;
56 header_font = headerfont;
57 header_font_selected = headerfont_selected;
58 menu_font = menufont;
59 menu_font_selected = menufont_selected;
60 shortcut_font = shortcutfont;
61 shortcut_font_selected = shortcutfont_selected;
62 }
63
64
get_current_menu()65 const Menu * get_current_menu()
66 {
67 return current_menu;
68 }
69
70
get_current_menu_action()71 const Menu * get_current_menu_action()
72 {
73 return current_menu_action;
74 }
75
close_menu()76 void close_menu()
77 {
78 if (current_menu_action == NULL)
79 {
80 if (menu_close_hook) menu_close_hook();
81 }
82 else
83 {
84 if (menu_close_hook) menu_close_hook();
85 if (current_menu_action->action == MENU_CHECK || current_menu_action->action == MENU_CHECK_NOSET)
86 {
87 if (current_menu_action->action == MENU_CHECK) *(int*)(current_menu_action->p1) ^= CASTPTR(int,current_menu_action->p2);
88
89 if (current_menu_action->p3)
90 ((void *(*)(void*,void*,void*))(current_menu_action->p3))(0,0,0);
91 }
92 else
93 {
94 current_menu_action->action(current_menu_action->p1, current_menu_action->p2, current_menu_action->p3);
95 }
96
97 current_menu = NULL;
98 current_menu_action = NULL;
99 }
100 }
101
102
get_menu_item_width(const Menu * item)103 static int get_menu_item_width(const Menu *item)
104 {
105 return strlen(item->text) + 1;
106 }
107
108
get_shortcut_key(const Menu * item)109 static const char * get_shortcut_key(const Menu *item)
110 {
111 if (!shortcuts) return NULL;
112
113 for (int i = 0 ; shortcuts[i].action ; ++i)
114 {
115 if (((item->action == MENU_CHECK || item->action == MENU_CHECK_NOSET) && (void*)shortcuts[i].action == item->p3) ||
116 (shortcuts[i].action == item->action &&
117 (void*)shortcuts[i].p1 == item->p1 &&
118 (void*)shortcuts[i].p2 == item->p2 &&
119 (void*)shortcuts[i].p3 == item->p3))
120 {
121 return get_shortcut_string(&shortcuts[i]);
122 }
123 else if (item->submenu)
124 {
125 return "�";
126 }
127 }
128
129 return NULL;
130 }
131
132
133 // Below is a two-pass combined event and drawing menu routine. It is two-pass (as opposed to other combined drawing
134 // handlers otherwhere in the project) so it can handle overlapping zones correctly. Otherwhere in the app there simply
135 // are no overlapping zones, menus however can overlap because of the cascading submenus etc.
136
draw_submenu(GfxDomain * menu_dest,const SDL_Event * event,const Menu * items,const Menu * child,SDL_Rect * child_position,int pass)137 static void draw_submenu(GfxDomain *menu_dest, const SDL_Event *event, const Menu *items, const Menu *child, SDL_Rect *child_position, int pass)
138 {
139 SDL_Rect area = { 0, 0, menu_dest->screen_w, shortcut_font->h + 4 + 1 };
140 SDL_Rect r;
141 const Font *font = NULL;
142 int horiz = 0;
143
144 /* In short: this first iterates upwards the tree until it finds the main menu (FILE, SHOW etc.)
145 Then it goes back level by level updating the collision/draw zones
146 */
147
148 if (items)
149 {
150 if (items[0].parent != NULL)
151 {
152 draw_submenu(menu_dest, event, items[0].parent, items, &area, pass);
153
154 font = menu_font;
155
156 area.w = area.h = 0;
157
158 const Menu * item = items;
159
160 for (; item->text ; ++item)
161 {
162 area.w = my_max(get_menu_item_width(item), area.w);
163 if (item->text[0])
164 area.h += font->h + 1;
165 else
166 area.h += SEPARATOR_HEIGHT;
167 }
168
169 area.w = area.w * font->w;
170 area.x += 3;
171 area.y += 4;
172
173 area.w += SC_SIZE;
174
175 if (area.w + area.x > menu_dest->screen_w)
176 area.x -= area.w + area.x - menu_dest->screen_w + 2;
177
178 copy_rect(&r, &area);
179
180 SDL_Rect bev;
181 copy_rect(&bev, &area);
182 adjust_rect(&bev, -6);
183
184 if (pass == DRAW) bevel(menu_dest, &bev, menu_gfx, BEV_MENU);
185
186 r.h = font->h + 1;
187 }
188 else
189 {
190 if (pass == DRAW) bevel(menu_dest, &area, menu_gfx, BEV_MENUBAR);
191
192 copy_rect(&r, &area);
193 adjust_rect(&r, 2);
194
195 font = header_font;
196
197 horiz = 1;
198
199 r.h = font->h;
200 }
201
202 const Menu * item = items;
203
204 for (; item->text ; ++item)
205 {
206 if (item->text[0])
207 {
208 if (horiz) font = header_font; else font = menu_font;
209
210 const char * sc_text = get_shortcut_key(item);
211 int bg = 0;
212
213 if (horiz) r.w = font->w * get_menu_item_width(item) + 8;
214
215 if (event->type == SDL_MOUSEMOTION && pass == ZONE)
216 {
217 if ((event->button.x >= r.x) && (event->button.y >= r.y)
218 && (event->button.x < r.x + r.w) && (event->button.y < r.y + r.h))
219 {
220 if (item->submenu)
221 {
222 current_menu = item->submenu;
223 current_menu_action = NULL;
224 bg = 1;
225 }
226 else if (item->action)
227 {
228 current_menu_action = item;
229 current_menu = items;
230 bg = 1;
231 }
232 }
233 else if (current_menu_action && item == current_menu_action)
234 {
235 current_menu_action = NULL;
236 }
237 }
238
239 if (item->submenu == child && child)
240 {
241 copy_rect(child_position, &r);
242 if (horiz) child_position->y += r.h;
243 else { child_position->x += r.w + 4; child_position->y -= 4; }
244 bg = 1;
245 }
246
247 int selected = 0;
248
249 if ((pass == DRAW) && (bg || (current_menu_action == item && current_menu_action)))
250 {
251 SDL_Rect bar;
252 copy_rect(&bar, &r);
253 adjust_rect(&bar, -1);
254 bar.h --;
255 bevel(menu_dest, &bar, menu_gfx, BEV_MENU_SELECTED);
256
257 font = horiz ? header_font_selected : menu_font_selected;
258 selected = 1;
259 }
260
261 if (pass == DRAW)
262 {
263 SDL_Rect text;
264 copy_rect(&text, &r);
265 text.x += font->w;
266 text.w -= font->w;
267 font_write(font, menu_dest, &text, item->text);
268
269 char tick_char[2] = { 0 };
270
271 if ((item->action == MENU_CHECK || item->action == MENU_CHECK_NOSET) && (*(int*)item->p1 & CASTPTR(int,item->p2)))
272 *tick_char = '�';
273 else if (item->flags & MENU_BULLET)
274 *tick_char = '^';
275
276 if (tick_char[0] != 0)
277 {
278 SDL_Rect tick;
279 copy_rect(&tick, &r);
280 tick.y = r.h / 2 + r.y - shortcut_font->h / 2;
281 font_write(selected ? shortcut_font_selected : shortcut_font, menu_dest, &tick, tick_char);
282 }
283 }
284
285 if (pass == DRAW && !horiz && sc_text)
286 {
287 r.x += r.w;
288 int tmpw = r.w, tmpx = r.x, tmpy = r.y;
289 r.w = SC_SIZE;
290 r.x -= strlen(sc_text) * shortcut_font->w;
291 r.y = r.h / 2 + r.y - shortcut_font->h / 2;
292 font_write(selected ? shortcut_font_selected : shortcut_font, menu_dest, &r, sc_text);
293 r.x = tmpx;
294 r.y = tmpy;
295 update_rect(&area, &r);
296 r.w = tmpw;
297 }
298 else update_rect(&area, &r);
299 }
300 else
301 {
302 separator(menu_dest, &area, &r, menu_gfx, BEV_SEPARATOR);
303 }
304 }
305 }
306 }
307
308
draw_menu(GfxDomain * dest,const SDL_Event * e)309 void draw_menu(GfxDomain *dest, const SDL_Event *e)
310 {
311 draw_submenu(dest, e, current_menu, NULL, NULL, ZONE);
312 draw_submenu(dest, e, current_menu, NULL, NULL, DRAW);
313 }
314