1 /*
2
3 Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4 and the "Aleph One" developers.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 This license is contained in the file "COPYING",
17 which is included with this source code; it is available online at
18 http://www.gnu.org/licenses/gpl.html
19
20 */
21
22 /*
23 * sdl_widgets.cpp - Widgets for SDL dialogs
24 *
25 * Written in 2000 by Christian Bauer
26 *
27 * Sept-Nov. 2001 (Woody Zenfell):
28 * Significant extensions to support more dynamic behavior and more useful layouts
29 *
30 * Mar 1, 2002 (Woody Zenfell):
31 * Moved w_levels here from shell_sdl; am using it in Setup Network Game box.
32 *
33 * May 16, 2002 (Woody Zenfell):
34 * changes to w_key to support assignment of mouse buttons as well as keys;
35 * also fixed mouse-movement-while-binding behavior.
36 *
37 * August 27, 2003 (Woody Zenfell):
38 * new w_enabling_toggle can enable/disable a bank of other widgets according to its state
39 * new w_file_chooser displays filename; allows selection of file (via FileSpecifier::ReadDialog())
40 */
41
42 #include "cseries.h"
43 #include "sdl_dialogs.h"
44 #include "network_dialog_widgets_sdl.h"
45 #include "sdl_fonts.h"
46 #include "sdl_widgets.h"
47 #include "resource_manager.h"
48
49 #include "shape_descriptors.h"
50 #include "screen_drawing.h"
51 #include "images.h"
52 #include "shell.h"
53 #include "world.h"
54 #include "SoundManager.h"
55 #include "interface.h"
56 #include "player.h"
57
58 #include "screen.h"
59
60 // ZZZ: for stringset business for modified w_select
61 #include "TextStrings.h"
62
63 #include "mouse.h" // (ZZZ) NUM_SDL_MOUSE_BUTTONS, SDLK_BASE_MOUSE_BUTTON
64 #include "joystick.h"
65
66 #include <sstream>
67
68 /*
69 * Widget base class
70 */
71
72 // ZZZ: initialize my additional storage elements
73 // (thought: I guess "widget" could be simplified, and have a subclass "useful_widget" to handle most things
74 // other than spacers. Spacers are common and I guess we're starting to eat a fair amount of storage for a
75 // widget that does nothing and draws nothing... oh well, at least RAM is cheap. ;) )
widget()76 widget::widget() : active(false), dirty(false), enabled(true), font(NULL), identifier(NONE), owning_dialog(NULL), saved_min_width(0), saved_min_height(0), associated_label(0)
77 {
78 rect.x = 0;
79 rect.y = 0;
80 rect.w = 0;
81 rect.h = 0;
82 }
83
widget(int theme_widget)84 widget::widget(int theme_widget) : active(false), dirty(false), enabled(true), font(get_theme_font(theme_widget, style)), identifier(NONE), owning_dialog(NULL), saved_min_width(0), saved_min_height(0), associated_label(0)
85 {
86 rect.x = 0;
87 rect.y = 0;
88 rect.w = 0;
89 rect.h = 0;
90 }
91
associate_label(w_label * label)92 void widget::associate_label(w_label *label)
93 {
94 associated_label = label;
95 }
96
label(const char * text)97 w_label *widget::label(const char *text)
98 {
99 if (!associated_label)
100 {
101 associated_label = new w_label(text);
102 associated_label->associate_widget(this);
103 }
104
105 return associated_label;
106 }
107
108 // ZZZ: enable/disable
set_enabled(bool inEnabled)109 void widget::set_enabled(bool inEnabled)
110 {
111 if(enabled != inEnabled) {
112 enabled = inEnabled;
113
114 // If we had the focus when we were disabled, we should not have the focus afterward.
115 if(active && !enabled)
116 owning_dialog->activate_next_widget();
117
118 // Assume we need a redraw to reflect new state
119 dirty = true;
120
121 if (associated_label)
122 associated_label->set_enabled(inEnabled);
123 }
124 }
125
set_active(bool new_active)126 void widget::set_active(bool new_active)
127 {
128 // Assume we need a redraw to reflect new state
129 if (enabled && (active != new_active))
130 dirty = true;
131 active = new_active;
132 }
133
place(const SDL_Rect & r,placement_flags flags)134 void widget::place(const SDL_Rect &r, placement_flags flags)
135 {
136 rect.h = r.h;
137 rect.y = r.y;
138
139 if (flags & placeable::kFill)
140 {
141 rect.x = r.x;
142 rect.w = r.w;
143 }
144 else
145 {
146 rect.w = saved_min_width;
147 if (flags & placeable::kAlignLeft)
148 {
149 rect.x = r.x;
150 }
151 else if (flags & placeable::kAlignRight)
152 {
153 rect.x = r.x + r.w - saved_min_width;
154 }
155 else
156 {
157 rect.x = r.x + (r.w - saved_min_width) / 2;
158 }
159 }
160 }
161
162
163 /*
164 * Static text
165 */
166
167 // ZZZ change: copy the given string instead of just pointing to it. Much easier for messages that change.
w_static_text(const char * t,int _theme_type)168 w_static_text::w_static_text(const char *t, int _theme_type) : widget(_theme_type), theme_type(_theme_type)
169 {
170 text = strdup(t);
171 rect.w = text_width(text, font, style);
172 rect.h = font->get_line_height();
173 saved_min_height = rect.h;
174 saved_min_width = rect.w;
175 }
176
draw(SDL_Surface * s) const177 void w_static_text::draw(SDL_Surface *s) const
178 {
179 uint32 pixel;
180 pixel = get_theme_color(theme_type, DEFAULT_STATE, 0);
181
182 draw_text(s, text, rect.x, rect.y + font->get_ascent(), pixel, font, style);
183 }
184
click(int x,int y)185 void w_label::click(int x, int y)
186 {
187 if (associated_widget)
188 associated_widget->click(0, 0);
189
190 }
191
draw(SDL_Surface * s) const192 void w_label::draw(SDL_Surface *s) const
193 {
194 int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
195 uint16 style = 0;
196 draw_text(s, text, rect.x, rect.y + font->get_ascent() + (rect.h - font->get_line_height()) / 2, get_theme_color(LABEL_WIDGET, state, FOREGROUND_COLOR), font, style);
197 }
198
199 // ZZZ addition: change text.
200 void
set_text(const char * t)201 w_static_text::set_text(const char* t) {
202 free(text);
203 text = strdup(t);
204 dirty = true;
205 }
206
207 // ZZZ addition: free text buffer.
~w_static_text()208 w_static_text::~w_static_text() {
209 free(text);
210 }
211
w_styled_text(const char * t,int _theme_type)212 w_styled_text::w_styled_text(const char *t, int _theme_type) : w_static_text(t, _theme_type), text_string(t) {
213 rect.w = font->styled_text_width(text_string, text_string.size(), style);
214 saved_min_width = rect.w;
215 }
216
set_text(const char * t)217 void w_styled_text::set_text(const char* t)
218 {
219 w_static_text::set_text(t);
220 text_string = t;
221 // maybe reset rect.width here, but parent w_static_text doesn't, so we won't either
222 }
223
draw(SDL_Surface * s) const224 void w_styled_text::draw(SDL_Surface *s) const
225 {
226 uint32 pixel;
227 pixel = get_theme_color(theme_type, DEFAULT_STATE, 0);
228
229 font->draw_styled_text(s, text_string, text_string.size(), rect.x, rect.y + font->get_ascent(), pixel, style);
230 }
231
draw(SDL_Surface * s) const232 void w_slider_text::draw(SDL_Surface *s) const
233 {
234 int state = associated_slider->enabled ? (associated_slider->active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
235 uint16 style = 0;
236 draw_text(s, text, rect.x, rect.y + font->get_ascent() + (rect.h - font->get_line_height()) / 2, get_theme_color(LABEL_WIDGET, state, FOREGROUND_COLOR), font, style);
237 }
238
239 /*
240 * Picture (PICT resource)
241 */
242
w_pict(int id)243 w_pict::w_pict(int id)
244 {
245 LoadedResource rsrc;
246 get_resource(FOUR_CHARS_TO_INT('P', 'I', 'C', 'T'), id, rsrc);
247 picture = picture_to_surface(rsrc);
248 if (picture) {
249 rect.w = static_cast<uint16>(picture->w);
250 rect.h = static_cast<uint16>(picture->h);
251 SDL_SetColorKey(picture, SDL_TRUE, SDL_MapRGB(picture->format, 0xff, 0xff, 0xff));
252 } else
253 rect.w = rect.h = 0;
254 }
255
~w_pict()256 w_pict::~w_pict()
257 {
258 if (picture)
259 SDL_FreeSurface(picture);
260 }
261
draw(SDL_Surface * s) const262 void w_pict::draw(SDL_Surface *s) const
263 {
264 if (picture)
265 SDL_BlitSurface(picture, NULL, s, const_cast<SDL_Rect *>(&rect));
266 }
267
268
269 /*
270 * Button
271 */
272
w_button_base(const char * t,action_proc p,void * a,int _type)273 w_button_base::w_button_base(const char *t, action_proc p, void *a, int _type) : widget(_type), text(t), proc(p), arg(a), down(false), pressed(false), type(_type)
274 {
275 rect.w = text_width(text.c_str(), font, style) + get_theme_space(_type, BUTTON_L_SPACE) + get_theme_space(_type, BUTTON_R_SPACE);
276 button_c_default = get_theme_image(_type, DEFAULT_STATE, BUTTON_C_IMAGE, rect.w - get_theme_image(_type, DEFAULT_STATE, BUTTON_L_IMAGE)->w - get_theme_image(_type, DEFAULT_STATE, BUTTON_R_IMAGE)->w);
277 button_c_active = get_theme_image(_type, ACTIVE_STATE, BUTTON_C_IMAGE, rect.w - get_theme_image(_type, ACTIVE_STATE, BUTTON_L_IMAGE)->w - get_theme_image(_type, ACTIVE_STATE, BUTTON_R_IMAGE)->w);
278 button_c_disabled = get_theme_image(_type, DISABLED_STATE, BUTTON_C_IMAGE, rect.w - get_theme_image(_type, DISABLED_STATE, BUTTON_L_IMAGE)->w - get_theme_image(_type, DISABLED_STATE, BUTTON_R_IMAGE)->w);
279 button_c_pressed = get_theme_image(_type, PRESSED_STATE, BUTTON_C_IMAGE, rect.w - get_theme_image(_type, PRESSED_STATE, BUTTON_L_IMAGE)->w - get_theme_image(_type, PRESSED_STATE, BUTTON_R_IMAGE)->w);
280
281 rect.h = static_cast<uint16>(get_theme_space(_type, BUTTON_HEIGHT));
282
283 saved_min_width = rect.w;
284 saved_min_height = rect.h;
285 }
286
~w_button_base()287 w_button_base::~w_button_base()
288 {
289 if (button_c_default) SDL_FreeSurface(button_c_default);
290 if (button_c_active) SDL_FreeSurface(button_c_active);
291 if (button_c_disabled) SDL_FreeSurface(button_c_disabled);
292 if (button_c_pressed) SDL_FreeSurface(button_c_pressed);
293 }
294
set_callback(action_proc p,void * a)295 void w_button_base::set_callback(action_proc p, void *a)
296 {
297 proc = p;
298 arg = a;
299 }
300
draw(SDL_Surface * s) const301 void w_button_base::draw(SDL_Surface *s) const
302 {
303 // Label (ZZZ: different color for disabled)
304 int state = DEFAULT_STATE;
305 if (pressed)
306 state = PRESSED_STATE;
307 else if (!enabled)
308 state = DISABLED_STATE;
309 else if (active)
310 state = ACTIVE_STATE;
311
312 if (use_theme_images(type))
313 {
314 SDL_Surface *button_l = get_theme_image(type, state, BUTTON_L_IMAGE);
315 SDL_Surface *button_r = get_theme_image(type, state, BUTTON_R_IMAGE);
316 SDL_Surface *button_c = button_c_default;
317 if (pressed)
318 button_c = button_c_pressed;
319 else if (!enabled)
320 button_c = button_c_disabled;
321 else if (active)
322 button_c = button_c_active;
323
324 // Button image
325 SDL_Rect r = {rect.x, rect.y,
326 static_cast<Uint16>(button_l->w),
327 static_cast<Uint16>(button_l->h)};
328 SDL_BlitSurface(button_l, NULL, s, &r);
329 r.x = r.x + static_cast<Sint16>(button_l->w); // MDA: MSVC throws warnings if we use +=
330 r.w = static_cast<Uint16>(button_c->w);
331 r.h = static_cast<Uint16>(button_c->h);
332 SDL_BlitSurface(button_c, NULL, s, &r);
333 r.x = r.x + static_cast<Sint16>(button_c->w);
334 r.w = static_cast<Uint16>(button_r->w);
335 r.h = static_cast<Uint16>(button_r->h);
336 SDL_BlitSurface(button_r, NULL, s, &r);
337 }
338 else
339 {
340 uint32 pixel;
341 if (use_theme_color(type, BACKGROUND_COLOR))
342 {
343 uint32 pixel = get_theme_color(type, state, BACKGROUND_COLOR);
344 SDL_Rect r = {rect.x + 1, rect.y + 1, rect.w - 2, rect.h - 2};
345 SDL_FillRect(s, &r, pixel);
346 }
347
348 pixel = get_theme_color(type, state, FRAME_COLOR);
349 draw_rectangle(s, &rect, pixel);
350 }
351
352 draw_text(s, text.c_str(), rect.x + get_theme_space(type, BUTTON_L_SPACE),
353 rect.y + get_theme_space(type, BUTTON_T_SPACE) + font->get_ascent(),
354 get_theme_color(type, state), font, style);
355 }
356
mouse_move(int x,int y)357 void w_button_base::mouse_move(int x, int y)
358 {
359 if (down)
360 {
361 if (x >= 0 && x <= rect.w && y >= 0 && y <= rect.h)
362 {
363 if (!pressed)
364 dirty = true;
365 pressed = true;
366 }
367 else
368 {
369 if (pressed)
370 dirty = true;
371 pressed = false;
372 }
373 get_owning_dialog()->draw_dirty_widgets();
374 }
375 }
376
mouse_down(int,int)377 void w_button_base::mouse_down(int, int)
378 {
379 if (!enabled) return;
380 down = true;
381 pressed = true;
382 dirty = true;
383 get_owning_dialog()->draw_dirty_widgets();
384 }
385
mouse_up(int x,int y)386 void w_button_base::mouse_up(int x, int y)
387 {
388 if (!enabled) return;
389
390 down = false;
391 pressed = false;
392 dirty = true;
393 get_owning_dialog()->draw_dirty_widgets();
394
395 if (proc && x >= 0 && x <= rect.w && y >= 0 && y <= rect.h)
396 proc(arg);
397 }
398
click(int,int)399 void w_button_base::click(int /*x*/, int /*y*/)
400 {
401 // simulate a mouse press
402 mouse_down(0, 0);
403 SDL_Delay(1000 / 12);
404 mouse_up(0, 0);
405 }
406
407 /*
408 * Clickable link
409 */
410
prochandler(void * arg)411 void w_hyperlink::prochandler(void *arg)
412 {
413 toggle_fullscreen(false);
414 launch_url_in_browser(static_cast<const char *>(arg));
415 get_owning_dialog()->draw();
416 }
417
w_hyperlink(const char * url,const char * txt)418 w_hyperlink::w_hyperlink(const char *url, const char *txt) : w_button_base((txt ? txt : url), boost::bind(&w_hyperlink::prochandler, this, _1), const_cast<char *>(url), HYPERLINK_WIDGET)
419 {
420 rect.w = text_width(text.c_str(), font, style);
421 rect.h = font->get_line_height();
422 saved_min_height = rect.h;
423 saved_min_width = rect.w;
424 }
425
draw(SDL_Surface * s) const426 void w_hyperlink::draw(SDL_Surface *s) const
427 {
428 int state = DEFAULT_STATE;
429 if (pressed)
430 state = PRESSED_STATE;
431 else if (!enabled)
432 state = DISABLED_STATE;
433 else if (active)
434 state = ACTIVE_STATE;
435
436 uint32 pixel = get_theme_color(HYPERLINK_WIDGET, state, 0);
437
438 draw_text(s, text.c_str(), rect.x, rect.y + font->get_ascent(), pixel, font, style);
439
440 // draw_text doesn't support underline, so draw one manually
441 if (style & styleUnderline)
442 {
443 SDL_Rect r = {rect.x, rect.y + rect.h - 1, rect.w, 1};
444 SDL_FillRect(s, &r, pixel);
445 }
446 }
447
448
449 /*
450 * Tabs
451 */
452
w_tab(const vector<string> & _labels,tab_placer * _placer)453 w_tab::w_tab(const vector<string>& _labels, tab_placer *_placer) : widget(TAB_WIDGET), labels(_labels), placer(_placer), active_tab(1), pressed_tab(0)
454 {
455 saved_min_height = get_theme_space(TAB_WIDGET, BUTTON_HEIGHT);
456 for (vector<string>::iterator it = labels.begin(); it != labels.end(); ++it)
457 {
458 int l_space = (it == labels.begin()) ? get_theme_space(TAB_WIDGET, BUTTON_L_SPACE) : get_theme_space(TAB_WIDGET, TAB_LC_SPACE);
459 int r_space = (it == labels.end() - 1) ? get_theme_space(TAB_WIDGET, BUTTON_R_SPACE) : get_theme_space(TAB_WIDGET, TAB_RC_SPACE);
460 int width = l_space + r_space + font->text_width(it->c_str(), style);
461 widths.push_back(width);
462 saved_min_width += width;
463
464 // load center images
465 const int states[] = { DEFAULT_STATE, PRESSED_STATE, ACTIVE_STATE, DISABLED_STATE };
466 images.resize(PRESSED_STATE + 1);
467 for (int i = 0; i < 4; ++i)
468 {
469 int li_space = (it == labels.begin()) ? get_theme_image(TAB_WIDGET, states[i], TAB_L_IMAGE)->w : get_theme_image(TAB_WIDGET, states[i], TAB_LC_IMAGE)->w;
470 int ri_space = (it == labels.end() - 1) ? get_theme_image(TAB_WIDGET, states[i], TAB_R_IMAGE)->w : get_theme_image(TAB_WIDGET, states[i], TAB_RC_IMAGE)->w;
471 int c_space = width - li_space - ri_space;
472 images[states[i]].push_back(get_theme_image(TAB_WIDGET, states[i], TAB_C_IMAGE, c_space));
473 }
474 }
475
476 }
477
~w_tab()478 w_tab::~w_tab()
479 {
480 for (std::vector<std::vector<SDL_Surface *> >::iterator it = images.begin(); it != images.end(); ++it)
481 {
482 for (std::vector<SDL_Surface *>::iterator it2 = it->begin(); it2 != it->end(); ++it2)
483 {
484 SDL_FreeSurface(*it2);
485 }
486 }
487 }
488
draw(SDL_Surface * s) const489 void w_tab::draw(SDL_Surface *s) const
490 {
491 int x = rect.x;
492 for (int i = 0; i < labels.size(); ++i)
493 {
494 int state;
495 if (!enabled)
496 state = DISABLED_STATE;
497 else if (i == pressed_tab)
498 state = PRESSED_STATE;
499 else if (active && i == active_tab)
500 state = ACTIVE_STATE;
501 else
502 state = DEFAULT_STATE;
503
504 int l_space;
505 int l_offset = 0;
506
507 SDL_Surface *l_image;
508 if (i == 0)
509 {
510 l_space = get_theme_space(TAB_WIDGET, BUTTON_L_SPACE);
511 l_offset = 1;
512 l_image = get_theme_image(TAB_WIDGET, state, TAB_L_IMAGE);
513 }
514 else
515 {
516 l_space = get_theme_space(TAB_WIDGET, TAB_LC_SPACE);
517 l_image = get_theme_image(TAB_WIDGET, state, TAB_LC_IMAGE);
518 }
519
520 int r_space;
521 int r_offset = 0;
522 SDL_Surface *r_image;
523 if (i == labels.size() - 1)
524 {
525 r_space = get_theme_space(TAB_WIDGET, BUTTON_R_SPACE);
526 r_offset = 1;
527 r_image = get_theme_image(TAB_WIDGET, state, TAB_R_IMAGE);
528 }
529 else
530 {
531 r_space = get_theme_space(TAB_WIDGET, TAB_RC_SPACE);
532 r_image = get_theme_image(TAB_WIDGET, state, TAB_RC_IMAGE);
533 }
534
535 int c_space;
536 SDL_Surface *c_image = images[state][i];
537 c_space = font->text_width(labels[i].c_str(), style);
538
539 if (use_theme_images(TAB_WIDGET))
540 {
541 SDL_Rect r = { x, rect.y, static_cast<Uint16>(l_image->w), static_cast<Uint16>(l_image->h) };
542 SDL_BlitSurface(l_image, NULL, s, &r);
543 r.x = r.x + static_cast<Sint16>(l_image->w);
544 r.w = static_cast<Uint16>(c_image->w);
545 r.h = static_cast<Uint16>(c_image->h);
546 SDL_BlitSurface(c_image, NULL, s, &r);
547 r.x = r.x + static_cast<Sint16>(c_image->w);
548 r.w = static_cast<Uint16>(r_image->w);
549 r.h = static_cast<Uint16>(r_image->h);
550 SDL_BlitSurface(r_image, NULL, s, &r);
551 }
552 else
553 {
554 if (use_theme_color(TAB_WIDGET, BACKGROUND_COLOR))
555 {
556 SDL_Rect r = { x + l_offset, rect.y + 1, l_space + c_space + r_space - l_offset - r_offset, get_theme_space(TAB_WIDGET, BUTTON_HEIGHT) - 2 };
557 uint32 pixel = get_theme_color(TAB_WIDGET, state, BACKGROUND_COLOR);
558 SDL_FillRect(s, &r, pixel);
559 }
560 }
561
562 font->draw_text(s, labels[i].c_str(), labels[i].size(), x + l_space, rect.y + get_theme_space(TAB_WIDGET, BUTTON_T_SPACE) + font->get_ascent(), get_theme_color(TAB_WIDGET, state, FOREGROUND_COLOR), style);
563
564 x += l_space + c_space + r_space;
565 }
566
567 if (!use_theme_images(TAB_WIDGET))
568 {
569 uint32 pixel = get_theme_color(TAB_WIDGET, DEFAULT_STATE, FRAME_COLOR);
570 // draw the frame
571 SDL_Rect frame = { rect.x, rect.y, x - rect.x, get_theme_space(TAB_WIDGET, BUTTON_HEIGHT) };
572 draw_rectangle(s, &frame, pixel);
573 }
574 }
575
choose_tab(int i)576 void w_tab::choose_tab(int i)
577 {
578 pressed_tab = i;
579 active_tab = (i + 1) % labels.size();
580 placer->choose_tab(i);
581 get_owning_dialog()->draw();
582 }
583
click(int x,int y)584 void w_tab::click(int x, int y)
585 {
586 if (enabled)
587 {
588 if (!x && !y)
589 {
590 choose_tab(active_tab);
591 }
592 else
593 {
594 int width = 0;
595 for (int i = 0; i < labels.size(); ++i)
596 {
597 if (x > width && x < width + widths[i])
598 {
599 choose_tab(i);
600 return;
601 }
602
603 width += widths[i];
604 }
605 }
606 }
607 }
608
event(SDL_Event & e)609 void w_tab::event(SDL_Event& e)
610 {
611 if (e.type == SDL_KEYDOWN)
612 {
613 switch (e.key.keysym.sym) {
614 case SDLK_LEFT:
615 if (active_tab > 0)
616 {
617 if (active_tab - 1== pressed_tab)
618 {
619 if (pressed_tab > 0)
620 active_tab -= 2;
621 }
622 else
623 active_tab--;
624
625 }
626 dirty = true;
627 e.type = SDL_LASTEVENT;
628 break;
629
630 case SDLK_RIGHT:
631 if (active_tab < labels.size() - 1)
632 {
633 if (active_tab + 1 == pressed_tab)
634 {
635 if (pressed_tab < labels.size() - 1)
636 active_tab += 2;
637 }
638 else
639 active_tab++;
640 }
641 dirty = true;
642 e.type = SDL_LASTEVENT;
643 break;
644
645 default:
646 break;
647
648 }
649 }
650 else if (e.type == SDL_CONTROLLERBUTTONDOWN)
651 {
652 switch (e.cbutton.button) {
653 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
654 if (active_tab > 0)
655 {
656 if (active_tab - 1== pressed_tab)
657 {
658 if (pressed_tab > 0)
659 active_tab -= 2;
660 }
661 else
662 active_tab--;
663
664 }
665 dirty = true;
666 e.type = SDL_LASTEVENT;
667 break;
668
669 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
670 if (active_tab < labels.size() - 1)
671 {
672 if (active_tab + 1 == pressed_tab)
673 {
674 if (pressed_tab < labels.size() - 1)
675 active_tab += 2;
676 }
677 else
678 active_tab++;
679 }
680 dirty = true;
681 e.type = SDL_LASTEVENT;
682 break;
683
684 default:
685 break;
686
687 }
688 }
689 }
690
691 /*
692 * Selection button
693 */
694
695 const uint16 MAX_TEXT_WIDTH = 200;
696
697 // ZZZ: how come we have to do this? because of that "&" in the typedef for action_proc?
698 // Anyway, this fixes the "crash when clicking in the Environment menu" bug we've seen
699 // in the Windows version all this time.
w_select_button(const char * s,action_proc p,void * a,bool u)700 w_select_button::w_select_button(const char *s, action_proc p, void *a, bool u)
701 : widget(LABEL_WIDGET), selection(s), proc(p), arg(a), utf8(u), p_flags(placeable::kDefault)
702 {
703 uint16 max_selection_width = MAX_TEXT_WIDTH;
704
705 saved_min_width = max_selection_width;
706 saved_min_height = font->get_line_height();
707 }
708
709
710
draw(SDL_Surface * s) const711 void w_select_button::draw(SDL_Surface *s) const
712 {
713 int y = rect.y + font->get_ascent();
714
715 // Selection (ZZZ: different color for disabled)
716 set_drawing_clip_rectangle(0, rect.x + selection_x, static_cast<uint16>(s->h), rect.x + rect.w);
717
718 int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
719
720 draw_text(s, selection, rect.x + selection_x, y, get_theme_color(ITEM_WIDGET, state), font, style, utf8);
721 set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
722
723 // Cursor
724 if (active) {
725 //!!
726 }
727 }
728
click(int,int)729 void w_select_button::click(int /*x*/, int /*y*/)
730 {
731 if(enabled)
732 proc(arg);
733 }
734
set_selection(const char * s)735 void w_select_button::set_selection(const char *s)
736 {
737 selection = s;
738 if (p_flags & placeable::kAlignRight)
739 {
740 selection_x = rect.w - text_width(selection, font, style);
741 }
742 dirty = true;
743 }
744
place(const SDL_Rect & r,placement_flags flags)745 void w_select_button::place(const SDL_Rect &r, placement_flags flags)
746 {
747 rect.h = r.h;
748 rect.y = r.y;
749
750 rect.x = r.x;
751 rect.w = r.w;
752 p_flags = flags;
753
754 if (flags & placeable::kAlignRight)
755 {
756 selection_x = rect.w - text_width(selection, font, style);
757 }
758 else
759 {
760 selection_x = 0;
761 }
762
763 }
764
765 /*
766 * Selection widget (base class)
767 */
768
769 // ZZZ: change of behavior/semantics:
770 // if passed an invalid selection, reset to a VALID one (i.e. 0)
771 // if no valid labels, returns -1 when asked for selection
772 // draw(), get_selection() check num_labels directly instead of trying to keep selection set at -1
773
774 static const char* sNoValidOptionsString = "(no valid options)"; // XXX should be moved outside compiled code e.g. to MML
775
w_select(size_t s,const char ** l)776 w_select::w_select(size_t s, const char **l) : widget(LABEL_WIDGET), labels(l), we_own_labels(false), selection(s), selection_changed_callback(NULL), utf8(false)
777 {
778 num_labels = 0;
779 if(labels) {
780 while (labels[num_labels])
781 num_labels++;
782 if (selection >= num_labels || selection < 0)
783 selection = 0;
784 }
785
786 saved_min_height = font->get_line_height();
787 }
788
789
~w_select()790 w_select::~w_select() {
791 if(we_own_labels && labels)
792 free(labels);
793 }
794
place(const SDL_Rect & r,placement_flags flags)795 void w_select::place(const SDL_Rect& r, placement_flags flags)
796 {
797 rect.h = r.h;
798 rect.y = r.y;
799 rect.x = r.x;
800 rect.w = r.w;
801
802 }
803
min_width()804 int w_select::min_width()
805 {
806 return get_largest_label_width();
807 }
808
draw(SDL_Surface * s) const809 void w_select::draw(SDL_Surface *s) const
810 {
811 int y = rect.y + font->get_ascent() + (rect.h - font->get_line_height()) / 2;
812
813 // Selection (ZZZ: different color for disabled)
814 const char *str = (num_labels > 0 ? labels[selection] : sNoValidOptionsString);
815
816 int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
817
818 draw_text(s, str, rect.x, y, get_theme_color(ITEM_WIDGET, state), font, style, utf8);
819
820 // Cursor
821 if (active) {
822 //!!
823 }
824 }
825
click(int,int)826 void w_select::click(int /*x*/, int /*y*/)
827 {
828 if(enabled) {
829 selection++;
830 if (selection >= num_labels)
831 selection = 0;
832
833 selection_changed();
834 }
835 }
836
event(SDL_Event & e)837 void w_select::event(SDL_Event &e)
838 {
839 if (e.type == SDL_KEYDOWN) {
840 if (e.key.keysym.sym == SDLK_LEFT) {
841 if (num_labels == 0)
842 selection = 0;
843 else if (selection == 0)
844 selection = num_labels - 1;
845 else
846 selection--;
847 selection_changed();
848 e.type = SDL_LASTEVENT; // Swallow event
849 } else if (e.key.keysym.sym == SDLK_RIGHT) {
850 if (selection >= num_labels - 1)
851 selection = 0;
852 else
853 selection++;
854 selection_changed();
855 e.type = SDL_LASTEVENT; // Swallow event
856 }
857 } else if (e.type == SDL_CONTROLLERBUTTONDOWN) {
858 if (e.cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) {
859 if (num_labels == 0)
860 selection = 0;
861 else if (selection == 0)
862 selection = num_labels - 1;
863 else
864 selection--;
865 selection_changed();
866 e.type = SDL_LASTEVENT; // Swallow event
867 } else if (e.cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) {
868 if (selection >= num_labels - 1)
869 selection = 0;
870 else
871 selection++;
872 selection_changed();
873 e.type = SDL_LASTEVENT; // Swallow event
874 }
875 }
876 }
877
set_selection(size_t s,bool simulate_user_input)878 void w_select::set_selection(size_t s, bool simulate_user_input /* default: false */)
879 {
880 //if (s >= num_labels || s < 0)
881 // s = 0;
882 assert(s == PIN(s, 0, num_labels - 1));
883
884 if(selection != s) {
885 selection = s;
886
887 if(simulate_user_input)
888 selection_changed();
889 }
890
891 dirty = true;
892 }
893
894 // ZZZ: change labels after creation
set_labels(const char ** inLabels)895 void w_select::set_labels(const char** inLabels) {
896 if(we_own_labels && labels != NULL)
897 free(labels);
898
899 labels = inLabels;
900 num_labels = 0;
901 if(labels) {
902 while(inLabels[num_labels])
903 num_labels++;
904 }
905 we_own_labels = false;
906 if (selection >= num_labels)
907 set_selection(0);
908 else
909 set_selection(selection);
910
911 // Hope new labels have same max width as old, or that user called set_full_width() on us.
912 }
913
914
915 // ZZZ: set labels according to stringset
set_labels_stringset(short inStringSetID)916 void w_select::set_labels_stringset(short inStringSetID) {
917 // free old label pointers, if appropriate
918 if(we_own_labels && labels != NULL)
919 free(labels);
920
921 // see if we need space for label pointers. if so, allocate and fill them in.
922 if(TS_IsPresent(inStringSetID) && TS_CountStrings(inStringSetID) > 0) {
923 num_labels = TS_CountStrings(inStringSetID);
924
925 // Allocate one extra pointer slot to fill with NULL
926 // Note: this works right if the string count is 0... but we check anyway, can save a malloc/free.
927 labels = (const char**)malloc(sizeof(const char*) * (num_labels + 1));
928 labels[num_labels] = NULL;
929
930 for(size_t i = 0; i < num_labels; i++) {
931 // shared references should be OK, stringsets ought to be pretty stable. No need to copy...
932 labels[i] = TS_GetCString(inStringSetID, i);
933 }
934
935 // we allocated; we free.
936 we_own_labels = true;
937 }
938 else { // no stringset or no strings in stringset
939 labels = NULL;
940 num_labels = 0;
941 we_own_labels = false;
942 }
943
944 if (selection >= num_labels)
945 set_selection(0);
946 else
947 set_selection(selection);
948
949 // Hope new labels have same max width as old, or that user called set_full_width() on us.
950 }
951
selection_changed(void)952 void w_select::selection_changed(void)
953 {
954 play_dialog_sound(DIALOG_CLICK_SOUND);
955 dirty = true;
956
957 // ZZZ: call user-specified callback
958 if(selection_changed_callback != NULL)
959 selection_changed_callback(this);
960 }
961
962
963 // ZZZ addition
get_largest_label_width()964 uint16 w_select::get_largest_label_width() {
965 uint16 max_label_width = 0;
966 for (size_t i=0; i<num_labels; i++) {
967 uint16 width = text_width(labels[i], font, style, utf8);
968 if (width > max_label_width)
969 max_label_width = width;
970 }
971
972 // ZZZ: account for "no valid options" string
973 if(num_labels <= 0)
974 max_label_width = text_width(sNoValidOptionsString, font, style, utf8);
975
976 return max_label_width;
977 }
978
979
980 /*
981 * On-off toggle
982 */
983
984 const char *w_toggle::onoff_labels[] = {"\342\230\220", "\342\230\221", NULL };
985
w_toggle(bool selection,const char ** labels)986 w_toggle::w_toggle(bool selection, const char **labels) : w_select(selection, labels) {
987 if (labels == onoff_labels && use_theme_images(CHECKBOX))
988 {
989 saved_min_height = get_theme_space(CHECKBOX, BUTTON_HEIGHT);
990 }
991 else if (labels == onoff_labels)
992 {
993 labels_are_utf8(true);
994 font = get_theme_font(CHECKBOX, style);
995 saved_min_height = get_theme_space(CHECKBOX, BUTTON_HEIGHT);
996 }
997 }
998
draw(SDL_Surface * s) const999 void w_toggle::draw(SDL_Surface *s) const
1000 {
1001 // Selection (ZZZ: different color for disabled)
1002 const char *str = (num_labels > 0 ? labels[selection] : sNoValidOptionsString);
1003
1004 int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
1005
1006 if (labels == onoff_labels && use_theme_images(CHECKBOX))
1007 {
1008 SDL_Surface *image = get_theme_image(CHECKBOX, state, selection);
1009 SDL_Rect r = { rect.x, rect.y + (rect.h - saved_min_height) / 2 + get_theme_space(CHECKBOX, BUTTON_T_SPACE), image->w, image->h };
1010 SDL_BlitSurface(image, 0, s, &r);
1011 }
1012 else if (labels == onoff_labels)
1013 {
1014 draw_text(s, str, rect.x, rect.y + (rect.h - saved_min_height) / 2 + get_theme_space(CHECKBOX, BUTTON_T_SPACE), get_theme_color(LABEL_WIDGET, state, FOREGROUND_COLOR), font, style, utf8);
1015 }
1016 else
1017 {
1018 draw_text(s, str, rect.x, rect.y + font->get_ascent(), get_theme_color(ITEM_WIDGET, state), font, style, utf8);
1019 }
1020
1021 // Cursor
1022 if (active) {
1023 //!!
1024 }
1025 }
1026
min_width()1027 int w_toggle::min_width()
1028 {
1029 if (labels == onoff_labels && use_theme_images(CHECKBOX))
1030 {
1031 return get_theme_image(CHECKBOX, DEFAULT_STATE, 0)->w;
1032 }
1033 else
1034 {
1035 return w_select::min_width();
1036 }
1037 }
1038
1039
1040 /*
1041 * Enabling toggle (ZZZ)
1042 *
1043 * Can enable/disable a bank of other widgets according to its state
1044 */
1045
1046
w_enabling_toggle(bool inSelection,bool inEnablesWhenOn,const char ** inLabels)1047 w_enabling_toggle::w_enabling_toggle(bool inSelection, bool inEnablesWhenOn, const char** inLabels)
1048 : w_toggle(inSelection, inLabels), enables_when_on(inEnablesWhenOn)
1049 {
1050 }
1051
1052
1053 void
selection_changed()1054 w_enabling_toggle::selection_changed()
1055 {
1056 w_toggle::selection_changed();
1057
1058 for(DependentCollection::iterator i = dependents.begin(); i != dependents.end(); i++)
1059 update_widget_enabled(*i);
1060 }
1061
1062
1063 /*
1064 * Player color selection
1065 */
1066
w_player_color(int selection)1067 w_player_color::w_player_color(int selection) : w_select(selection, NULL)
1068 {
1069 set_labels_stringset(kTeamColorsStringSetID);
1070 }
1071
draw(SDL_Surface * s) const1072 void w_player_color::draw(SDL_Surface *s) const
1073 {
1074 int y = rect.y + font->get_ascent();
1075
1076 // Selection
1077 if (selection >= 0) {
1078 uint32 pixel = get_dialog_player_color(selection);
1079 SDL_Rect r = {rect.x, rect.y + 1, 48, rect.h - 2};
1080 SDL_FillRect(s, &r, pixel);
1081 } else {
1082 int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
1083
1084 draw_text(s, "<unknown>", rect.x, y, get_theme_color(ITEM_WIDGET, state), font, style);
1085 }
1086
1087 // Cursor
1088 if (active) {
1089 //!!
1090 }
1091 }
1092
1093 class w_color_block : public widget {
1094 public:
w_color_block(const rgb_color * color)1095 w_color_block(const rgb_color *color) : m_color(color) {
1096 saved_min_height = 64;
1097 saved_min_width = 64;
1098 }
1099
draw(SDL_Surface * s) const1100 void draw(SDL_Surface *s) const {
1101 uint32 pixel = SDL_MapRGB(s->format, m_color->red >> 8, m_color->green >> 8, m_color->blue >> 8);
1102 SDL_Rect r = { rect.x, rect.y, 64, 64 };
1103 SDL_FillRect(s, &r, pixel);
1104 }
1105
is_dirty()1106 bool is_dirty() { return true; }
1107 private:
1108 const rgb_color *m_color;
1109 };
1110
1111
click(int,int)1112 void w_color_picker::click(int, int)
1113 {
1114 if (!enabled) return;
1115 dialog d;
1116
1117 vertical_placer *placer = new vertical_placer;
1118 placer->dual_add(new w_title("CHOOSE A COLOR"), d);
1119 placer->add(new w_spacer(), true);
1120
1121 w_color_block *color_block = new w_color_block(&m_color);
1122 placer->dual_add(color_block, d);
1123
1124 table_placer *table = new table_placer(2, get_theme_space(ITEM_WIDGET));
1125 table->col_flags(0, placeable::kAlignRight);
1126
1127 w_percentage_slider *red_w = new w_percentage_slider(16, m_color.red >> 12);
1128 table->dual_add(red_w->label("Red"), d);
1129 table->dual_add(red_w, d);
1130
1131 w_percentage_slider *green_w = new w_percentage_slider(16, m_color.green >> 12);
1132 table->dual_add(green_w->label("Green"), d);
1133 table->dual_add(green_w, d);
1134
1135 w_percentage_slider *blue_w = new w_percentage_slider(16, m_color.blue >> 12);
1136 table->dual_add(blue_w->label("Blue"), d);
1137 table->dual_add(blue_w, d);
1138
1139 placer->add(table, true);
1140 placer->add(new w_spacer(), true);
1141
1142 horizontal_placer *button_placer = new horizontal_placer;
1143 button_placer->dual_add(new w_button("CANCEL", dialog_cancel, &d), d);
1144 button_placer->dual_add(new w_button("OK", dialog_ok, &d), d);
1145
1146 placer->add(button_placer, true);
1147
1148 rgb_color old_color = m_color;
1149
1150 d.set_widget_placer(placer);
1151 d.set_processing_function(w_color_picker::update_color(red_w, green_w, blue_w, &m_color.red, &m_color.green, &m_color.blue));
1152
1153 if (d.run() == 0)
1154 {
1155 m_color.red = red_w->get_selection() << 12;
1156 m_color.green = green_w->get_selection() << 12;
1157 m_color.blue = blue_w->get_selection() << 12;
1158
1159 dirty = true;
1160 get_owning_dialog()->draw_dirty_widgets();
1161 }
1162 else
1163 {
1164 m_color = old_color;
1165 }
1166 }
1167
draw(SDL_Surface * s) const1168 void w_color_picker::draw(SDL_Surface *s) const
1169 {
1170 uint32 pixel = SDL_MapRGB(s->format, m_color.red >> 8, m_color.green >> 8, m_color.blue >> 8);
1171 SDL_Rect r = {rect.x, rect.y + 1, 48, rect.h - 2 };
1172 SDL_FillRect(s, &r, pixel);
1173 }
1174
1175 /*
1176 * Text entry widget
1177 */
1178
w_text_entry(size_t max_c,const char * initial_text)1179 w_text_entry::w_text_entry(size_t max_c, const char *initial_text)
1180 : widget(TEXT_ENTRY_WIDGET), enter_pressed_callback(NULL), value_changed_callback(NULL), max_chars(max_c), enable_mac_roman(false)
1181 {
1182 // Initialize buffer
1183 buf = new char[max_chars + 1];
1184 set_text(initial_text);
1185
1186 saved_min_width = MAX_TEXT_WIDTH;
1187
1188 saved_min_height = (int16) font->get_ascent() + font->get_descent() + font->get_leading();
1189 }
1190
~w_text_entry()1191 w_text_entry::~w_text_entry()
1192 {
1193 delete[] buf;
1194 }
1195
place(const SDL_Rect & r,placement_flags flags)1196 void w_text_entry::place(const SDL_Rect& r, placement_flags flags)
1197 {
1198 rect.h = (int16) font->get_ascent() + (int16) font->get_descent() + font->get_leading();
1199
1200 rect.y = r.y + (r.h - rect.h) / 2;
1201
1202 text_x = 0;
1203 rect.x = r.x;
1204 rect.w = r.w;
1205 max_text_width = rect.w;
1206 }
1207
draw(SDL_Surface * s) const1208 void w_text_entry::draw(SDL_Surface *s) const
1209 {
1210 int y = rect.y + font->get_ascent();
1211
1212 int16 theRectX = rect.x;
1213 uint16 theRectW = rect.w;
1214 int16 theTextX = text_x;
1215
1216 // Text
1217 int16 x = theRectX + theTextX;
1218 uint16 width = text_width(buf, font, style);
1219 if (width > max_text_width)
1220 x -= width - max_text_width;
1221 set_drawing_clip_rectangle(0, theRectX + theTextX, static_cast<uint16>(s->h), theRectX + theRectW);
1222
1223 int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
1224
1225 draw_text(s, buf, x, y, get_theme_color(TEXT_ENTRY_WIDGET, state), font, style);
1226 set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
1227
1228 // Cursor
1229 if (active) {
1230 width = text_width(buf, cursor_position, font, style);
1231 SDL_Rect r = {x + width - (width ? 1 : 0), rect.y, 1, rect.h};
1232 SDL_FillRect(s, &r, get_theme_color(TEXT_ENTRY_WIDGET, CURSOR_STATE));
1233 }
1234 }
1235
set_active(bool new_active)1236 void w_text_entry::set_active(bool new_active) {
1237 if (new_active && !active) {
1238 cursor_position = num_chars;
1239 SDL_StartTextInput();
1240 } else if (!new_active && active) {
1241 SDL_StopTextInput();
1242 }
1243 widget::set_active(new_active);
1244 }
1245
event(SDL_Event & e)1246 void w_text_entry::event(SDL_Event &e)
1247 {
1248 if (e.type == SDL_KEYDOWN) {
1249 switch (e.key.keysym.sym) {
1250 case SDLK_LEFT:
1251 left: if (cursor_position > 0) {
1252 cursor_position--;
1253 dirty = true;
1254 }
1255 e.type = SDL_LASTEVENT;
1256 break;
1257 case SDLK_RIGHT:
1258 right: if (cursor_position < num_chars) {
1259 cursor_position++;
1260 dirty = true;
1261 }
1262 e.type = SDL_LASTEVENT;
1263 break;
1264 case SDLK_RETURN:
1265 case SDLK_KP_ENTER:
1266 if (enter_pressed_callback)
1267 enter_pressed_callback(this);
1268 e.type = SDL_LASTEVENT;
1269 break;
1270 case SDLK_BACKSPACE:
1271 backspace: if (num_chars && cursor_position) {
1272 memmove(&buf[cursor_position - 1], &buf[cursor_position], num_chars - cursor_position);
1273 buf[--num_chars] = 0;
1274 --cursor_position;
1275 modified_text();
1276 play_dialog_sound(DIALOG_DELETE_SOUND);
1277 }
1278 e.type = SDL_LASTEVENT;
1279 break;
1280 case SDLK_DELETE:
1281 del: if (cursor_position < num_chars) {
1282 memmove(&buf[cursor_position], &buf[cursor_position + 1], num_chars - cursor_position - 1);
1283 buf[--num_chars] = 0;
1284 modified_text();
1285 play_dialog_sound(DIALOG_DELETE_SOUND);
1286 }
1287 e.type = SDL_LASTEVENT;
1288 break;
1289 case SDLK_HOME:
1290 home: if (cursor_position > 0) {
1291 cursor_position = 0;
1292 dirty = true;
1293 }
1294 e.type = SDL_LASTEVENT;
1295 break;
1296 case SDLK_END:
1297 end: if (cursor_position < num_chars) {
1298 cursor_position = num_chars;
1299 dirty = true;
1300 }
1301 e.type = SDL_LASTEVENT;
1302 break;
1303 case SDLK_UP:
1304 case SDLK_DOWN:
1305 break;
1306 case SDLK_a:
1307 if (e.key.keysym.mod & KMOD_CTRL)
1308 goto home;
1309 break;
1310 case SDLK_b:
1311 if (e.key.keysym.mod & KMOD_CTRL)
1312 goto left;
1313 break;
1314 case SDLK_d:
1315 if (e.key.keysym.mod & KMOD_CTRL)
1316 goto del;
1317 break;
1318 case SDLK_e:
1319 if (e.key.keysym.mod & KMOD_CTRL)
1320 goto end;
1321 break;
1322 case SDLK_f:
1323 if (e.key.keysym.mod & KMOD_CTRL)
1324 goto right;
1325 break;
1326 case SDLK_h:
1327 if (e.key.keysym.mod & KMOD_CTRL)
1328 goto backspace;
1329 break;
1330 case SDLK_k:
1331 if (e.key.keysym.mod & KMOD_CTRL) {
1332 if (cursor_position < num_chars) {
1333 num_chars = cursor_position;
1334 buf[num_chars] = 0;
1335 modified_text();
1336 play_dialog_sound(DIALOG_ERASE_SOUND);
1337 }
1338 }
1339 e.type = SDL_LASTEVENT;
1340 break;
1341 case SDLK_t:
1342 if (e.key.keysym.mod & KMOD_CTRL) {
1343 if (cursor_position) {
1344 if (cursor_position == num_chars)
1345 --cursor_position;
1346 char tmp = buf[cursor_position - 1];
1347 buf[cursor_position - 1] = buf[cursor_position];
1348 buf[cursor_position] = tmp;
1349 ++cursor_position;
1350 modified_text();
1351 play_dialog_sound(DIALOG_TYPE_SOUND);
1352 }
1353 }
1354 e.type = SDL_LASTEVENT;
1355 break;
1356 case SDLK_u:
1357 if (e.key.keysym.mod & KMOD_CTRL) {
1358 if (num_chars && cursor_position) {
1359 memmove(&buf[0], &buf[cursor_position], num_chars - cursor_position);
1360 num_chars -= cursor_position;
1361 buf[num_chars] = 0;
1362 cursor_position = 0;
1363 modified_text();
1364 play_dialog_sound(DIALOG_ERASE_SOUND);
1365 }
1366 }
1367 e.type = SDL_LASTEVENT;
1368 break;
1369 case SDLK_w:
1370 if (e.key.keysym.mod & KMOD_CTRL) {
1371 size_t erase_position = cursor_position;
1372 while (erase_position && buf[erase_position - 1] == ' ')
1373 --erase_position;
1374 while (erase_position && buf[erase_position - 1] != ' ')
1375 --erase_position;
1376 if (erase_position < cursor_position) {
1377 if (cursor_position < num_chars)
1378 memmove(&buf[erase_position], &buf[cursor_position], num_chars - cursor_position);
1379 num_chars -= cursor_position - erase_position;
1380 cursor_position = erase_position;
1381 modified_text();
1382 play_dialog_sound(DIALOG_ERASE_SOUND);
1383 }
1384 }
1385 e.type = SDL_LASTEVENT;
1386 break;
1387 default:
1388 break;
1389 }
1390 } else if (e.type == SDL_TEXTINPUT) {
1391 std::string input_utf8 = e.text.text;
1392 std::string input_roman = utf8_to_mac_roman(input_utf8);
1393 for (std::string::iterator it = input_roman.begin(); it != input_roman.end(); ++it)
1394 {
1395 uint16 uc = *it;
1396 if (uc >= ' ' && (uc < 0x80 || enable_mac_roman) && (num_chars + 1) < max_chars) {
1397 memmove(&buf[cursor_position + 1], &buf[cursor_position], num_chars - cursor_position);
1398 buf[cursor_position++] = static_cast<char>(uc);
1399 buf[++num_chars] = 0;
1400 modified_text();
1401 play_dialog_sound(DIALOG_TYPE_SOUND);
1402 }
1403 }
1404 e.type = SDL_LASTEVENT;
1405 }
1406 }
1407
click(int x,int y)1408 void w_text_entry::click(int x, int y)
1409 {
1410 bool was_active = active;
1411 get_owning_dialog()->activate_widget(this);
1412
1413 // Don't reposition cursor if:
1414 // - we were inactive before this click
1415 // - our text field is empty
1416 // - the click was simulated (0, 0) or out of bounds
1417 if (!was_active || !num_chars ||
1418 (x == 0 && y == 0) ||
1419 x < 0 || y < 0 || x >= rect.w || y >= rect.h) {
1420 return;
1421 }
1422
1423 // Find closest character boundary to click
1424 int width_remaining = x - text_x;
1425 size_t pos = 0;
1426 while (pos < num_chars && width_remaining > 0) {
1427 int cw = char_width(buf[pos], font, style);
1428 if (width_remaining > cw/2) {
1429 pos++; // right side is closer to target than left
1430 }
1431 width_remaining -= cw;
1432 }
1433 cursor_position = pos;
1434 dirty = true;
1435 }
1436
set_text(const char * text)1437 void w_text_entry::set_text(const char *text)
1438 {
1439 memset(buf, 0, max_chars + 1);
1440 if (text)
1441 strncpy(buf, text, max_chars);
1442 num_chars = strlen(buf);
1443 cursor_position = num_chars;
1444 modified_text();
1445 }
1446
modified_text(void)1447 void w_text_entry::modified_text(void)
1448 {
1449 dirty = true;
1450
1451 // ZZZ: callback if desired
1452 if(value_changed_callback)
1453 value_changed_callback(this);
1454 }
1455
1456 /*
1457 * Number entry widget
1458 */
1459
w_number_entry(int initial_number)1460 w_number_entry::w_number_entry(int initial_number) : w_text_entry(/*16*/4, NULL)
1461 {
1462 set_number(initial_number);
1463 saved_min_width = MAX_TEXT_WIDTH / 2;
1464 }
1465
event(SDL_Event & e)1466 void w_number_entry::event(SDL_Event &e)
1467 {
1468 if (e.type == SDL_TEXTINPUT) {
1469 std::string input_utf8 = e.text.text;
1470 std::string input_roman = utf8_to_mac_roman(input_utf8);
1471 for (std::string::iterator it = input_roman.begin(); it != input_roman.end(); ++it)
1472 {
1473 uint16 uc = *it;
1474 if (uc >= '0' && (uc < 0x80 || enable_mac_roman) && (num_chars + 1) < max_chars) {
1475 memmove(&buf[cursor_position + 1], &buf[cursor_position], num_chars - cursor_position);
1476 buf[cursor_position++] = static_cast<char>(uc);
1477 buf[++num_chars] = 0;
1478 modified_text();
1479 play_dialog_sound(DIALOG_TYPE_SOUND);
1480 }
1481 }
1482 }
1483
1484 w_text_entry::event(e);
1485 }
1486
set_number(int number)1487 void w_number_entry::set_number(int number)
1488 {
1489 char str[16];
1490 sprintf(str, "%d", number);
1491 set_text(str);
1492 }
1493
w_password_entry(size_t max_chars,const char * initial_text)1494 w_password_entry::w_password_entry(size_t max_chars, const char *initial_text) : w_text_entry(max_chars, initial_text)
1495 {
1496
1497 }
1498
draw(SDL_Surface * s) const1499 void w_password_entry::draw(SDL_Surface *s) const
1500 {
1501 string real_text = buf;
1502 memset(buf, '*', strlen(buf));
1503 w_text_entry::draw(s);
1504 strcpy(buf, real_text.c_str());
1505 }
1506
1507 /*
1508 * Key name widget
1509 */
1510
1511 static const std::vector<std::string> WAITING_TEXT = { "waiting for key", "waiting for button", "waiting for button" };
1512 static const std::vector<std::string> UNBOUND_TEXT = { "none", "none", "none" };
1513
w_key(SDL_Scancode key,w_key::Type event_type)1514 w_key::w_key(SDL_Scancode key, w_key::Type event_type) : widget(LABEL_WIDGET), binding(false), event_type(event_type)
1515 {
1516 set_key(key);
1517
1518 saved_min_width = text_width(WAITING_TEXT[event_type].c_str(), font, style);
1519 saved_min_height = font->get_line_height();
1520 }
1521
place(const SDL_Rect & r,placement_flags flags)1522 void w_key::place(const SDL_Rect& r, placement_flags flags)
1523 {
1524 rect.h = r.h;
1525 rect.y = r.y;
1526
1527 key_x = 0;
1528 rect.x = r.x;
1529 rect.w = r.w;
1530 }
1531
1532 // ZZZ: we provide phony key names for the phony keys used for mouse buttons.
1533 static const char* sMouseButtonKeyName[NUM_SDL_MOUSE_BUTTONS] = {
1534 "Mouse Left",
1535 "Mouse Middle",
1536 "Mouse Right",
1537 "Mouse X1",
1538 "Mouse X2",
1539 "Mouse Scroll Up",
1540 "Mouse Scroll Down"
1541 };
1542
1543 static const char* sJoystickButtonKeyName[NUM_SDL_JOYSTICK_BUTTONS] = {
1544 "A", "B", "X", "Y", "Back", "Guide", "Start",
1545 "LS", "RS", "LB", "RB", "Up", "Down", "Left", "Right",
1546 "LS Right", "LS Down", "RS Right", "RS Down", "LT", "RT",
1547 "LS Left", "LS Up", "RS Left", "RS Up", "LT Neg", "RT Neg"
1548 };
1549
1550 // ZZZ: this injects our phony key names but passes along the rest.
1551 static const char*
GetSDLKeyName(SDL_Scancode inKey)1552 GetSDLKeyName(SDL_Scancode inKey) {
1553 if (w_key::event_type_for_key(inKey) == w_key::MouseButton)
1554 return sMouseButtonKeyName[inKey - AO_SCANCODE_BASE_MOUSE_BUTTON];
1555 else if (w_key::event_type_for_key(inKey) == w_key::JoystickButton)
1556 return sJoystickButtonKeyName[inKey - AO_SCANCODE_BASE_JOYSTICK_BUTTON];
1557 else
1558 return SDL_GetScancodeName(inKey);
1559 }
1560
draw(SDL_Surface * s) const1561 void w_key::draw(SDL_Surface *s) const
1562 {
1563 int y = rect.y + font->get_ascent();
1564
1565 // Key
1566 int16 x = rect.x + key_x;
1567 if (binding) {
1568 draw_text(s, WAITING_TEXT[event_type].c_str(), x, y, get_theme_color(ITEM_WIDGET, ACTIVE_STATE), font, style);
1569 } else if (key == SDL_SCANCODE_UNKNOWN) {
1570 int state = enabled ? (active ? ACTIVE_STATE : DISABLED_STATE) : DISABLED_STATE;
1571 draw_text(s, UNBOUND_TEXT[event_type].c_str(), x, y, get_theme_color(ITEM_WIDGET, state), font, style);
1572 } else {
1573 int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
1574 draw_text(s, GetSDLKeyName(key), x, y, get_theme_color(ITEM_WIDGET, state), font, style);
1575 }
1576 }
1577
click(int,int)1578 void w_key::click(int /*x*/, int /*y*/)
1579 {
1580 get_owning_dialog()->activate_widget(this);
1581 if(enabled) {
1582 if (!binding) {
1583 binding = true;
1584 dirty = true;
1585 }
1586 }
1587 }
1588
set_active(bool new_active)1589 void w_key::set_active(bool new_active) {
1590 if (!new_active && binding) {
1591 binding = false;
1592 dirty = true;
1593 }
1594 widget::set_active(new_active);
1595 }
1596
event_type_for_key(SDL_Scancode key)1597 w_key::Type w_key::event_type_for_key(SDL_Scancode key) {
1598 if (key >= AO_SCANCODE_BASE_MOUSE_BUTTON && key < (AO_SCANCODE_BASE_MOUSE_BUTTON + NUM_SDL_MOUSE_BUTTONS))
1599 return MouseButton;
1600 else if (key >= AO_SCANCODE_BASE_JOYSTICK_BUTTON && key < (AO_SCANCODE_BASE_JOYSTICK_BUTTON + NUM_SDL_JOYSTICK_BUTTONS))
1601 return JoystickButton;
1602 return KeyboardKey;
1603 }
1604
event(SDL_Event & e)1605 void w_key::event(SDL_Event &e)
1606 {
1607 if(binding) {
1608 bool handled = false;
1609 bool up = false;
1610 switch (e.type) {
1611 case SDL_MOUSEBUTTONDOWN:
1612 if (event_type == MouseButton) {
1613 if (e.button.button < NUM_SDL_REAL_MOUSE_BUTTONS) {
1614 set_key(static_cast<SDL_Scancode>(AO_SCANCODE_BASE_MOUSE_BUTTON + e.button.button - 1));
1615 handled = true;
1616 }
1617 }
1618 break;
1619 case SDL_CONTROLLERBUTTONDOWN:
1620 if ((e.cbutton.button + AO_SCANCODE_BASE_JOYSTICK_BUTTON) == AO_SCANCODE_JOYSTICK_ESCAPE) {
1621 set_key(SDL_SCANCODE_UNKNOWN);
1622 handled = true;
1623 } else if (event_type == JoystickButton) {
1624 if (e.cbutton.button < SDL_CONTROLLER_BUTTON_MAX) {
1625 set_key(static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_BUTTON + e.cbutton.button));
1626 handled = true;
1627 }
1628 }
1629 break;
1630 case SDL_CONTROLLERAXISMOTION:
1631 if (event_type == JoystickButton) {
1632 if (e.caxis.value >= 16384) {
1633 set_key(static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_AXIS_POSITIVE + e.caxis.axis));
1634 handled = true;
1635 } else if (e.caxis.value <= -16384) {
1636 set_key(static_cast<SDL_Scancode>(AO_SCANCODE_BASE_JOYSTICK_AXIS_NEGATIVE + e.caxis.axis));
1637 handled = true;
1638 }
1639 }
1640 break;
1641 case SDL_MOUSEWHEEL:
1642 if (event_type == MouseButton) {
1643 up = (e.wheel.y > 0);
1644 #if SDL_VERSION_ATLEAST(2,0,4)
1645 if (e.wheel.direction == SDL_MOUSEWHEEL_FLIPPED)
1646 up = !up;
1647 #endif
1648 set_key(static_cast<SDL_Scancode>(up ? AO_SCANCODE_MOUSESCROLL_UP : AO_SCANCODE_MOUSESCROLL_DOWN));
1649 handled = true;
1650 }
1651 break;
1652 case SDL_KEYDOWN:
1653 if (e.key.keysym.scancode == SDL_SCANCODE_ESCAPE) {
1654 set_key(SDL_SCANCODE_UNKNOWN);
1655 handled = true;
1656 } else if (event_type == KeyboardKey) {
1657 set_key(e.key.keysym.scancode);
1658 handled = true;
1659 }
1660 break;
1661 case SDL_MOUSEMOTION:
1662 e.type = SDL_LASTEVENT; // suppress motion while assigning
1663 break;
1664 default:
1665 break;
1666 }
1667
1668 if (handled) {
1669 dirty = true;
1670 binding = false;
1671 // activate next widget by faking a key press
1672 e.type = SDL_KEYDOWN;
1673 e.key.keysym.sym = SDLK_DOWN;
1674 e.key.keysym.scancode = SDL_SCANCODE_DOWN;
1675 }
1676 }
1677 }
1678
1679
set_key(SDL_Scancode k)1680 void w_key::set_key(SDL_Scancode k)
1681 {
1682 key = k;
1683 }
1684
1685 /*
1686 * Progress
1687 */
draw(SDL_Surface * s) const1688 void w_progress_bar::draw(SDL_Surface* s) const
1689 {
1690 int filled_width = (rect.w - 2) * value / max_value;
1691 SDL_Rect dst_rect = rect;
1692 dst_rect.h -= 2;
1693 dst_rect.y += 2;
1694 SDL_FillRect(s, &dst_rect, get_theme_color(MESSAGE_WIDGET, DEFAULT_STATE, FOREGROUND_COLOR));
1695 dst_rect.x += filled_width + 1;
1696 dst_rect.y++;
1697 dst_rect.h -= 2;
1698 dst_rect.w = dst_rect.w - filled_width - 2;
1699 if (use_theme_color(DIALOG_FRAME, BACKGROUND_COLOR))
1700 SDL_FillRect(s, &dst_rect, get_theme_color(DIALOG_FRAME, DEFAULT_STATE, BACKGROUND_COLOR));
1701 }
1702
set_progress(int inValue,int inMaxValue)1703 void w_progress_bar::set_progress(int inValue, int inMaxValue)
1704 {
1705 value = inValue;
1706 max_value = inMaxValue;
1707 dirty = true;
1708 }
1709
1710
1711
1712 /*
1713 * Slider
1714 */
1715
1716 const int SLIDER_WIDTH = 160;
1717 const int SLIDER_THUMB_HEIGHT = 14;
1718 const int SLIDER_THUMB_WIDTH = 8;
1719 const int SLIDER_TROUGH_HEIGHT = 8;
1720 const int SLIDER_LABEL_SPACE = 5;
1721
w_slider(int num,int s)1722 w_slider::w_slider(int num, int s) : widget(LABEL_WIDGET), selection(s), num_items(num), thumb_dragging(false), readout(NULL), slider_changed_callback(NULL)
1723 {
1724 slider_l = get_theme_image(SLIDER_WIDGET, DEFAULT_STATE, SLIDER_L_IMAGE);
1725 slider_r = get_theme_image(SLIDER_WIDGET, DEFAULT_STATE, SLIDER_R_IMAGE);
1726 slider_c = get_theme_image(SLIDER_WIDGET, DEFAULT_STATE, SLIDER_C_IMAGE, SLIDER_WIDTH - slider_l->w - slider_r->w);
1727 thumb = get_theme_image(SLIDER_THUMB, DEFAULT_STATE, 0);
1728
1729 trough_width = SLIDER_WIDTH - get_theme_space(SLIDER_WIDGET, SLIDER_L_SPACE) - get_theme_space(SLIDER_WIDGET, SLIDER_R_SPACE);
1730
1731 saved_min_width = SLIDER_WIDTH;
1732 if (use_theme_images(SLIDER_WIDGET))
1733 saved_min_height = std::max(static_cast<uint16>(slider_c->h), static_cast<uint16>(thumb->h));
1734 else
1735 saved_min_height = SLIDER_THUMB_HEIGHT + 2;
1736
1737 readout_x = saved_min_width + SLIDER_LABEL_SPACE;
1738 init_formatted_value();
1739 }
1740
~w_slider()1741 w_slider::~w_slider()
1742 {
1743 if (slider_c) SDL_FreeSurface(slider_c);
1744 }
1745
place(const SDL_Rect & r,placement_flags flags)1746 void w_slider::place(const SDL_Rect& r, placement_flags flags)
1747 {
1748 rect.h = r.h;
1749 rect.y = r.y + (r.h - saved_min_height) / 2;
1750 rect.x = r.x;
1751 slider_x = 0;
1752 rect.w = r.w;
1753
1754 SDL_Rect r2;
1755 r2.h = r.h;
1756 r2.y = r.y;
1757 r2.x = r.x + readout_x;
1758 r2.w = readout->min_width();
1759 readout->place(r2, placeable::kDefault);
1760
1761 set_selection(selection);
1762 }
1763
draw(SDL_Surface * s) const1764 void w_slider::draw(SDL_Surface *s) const
1765 {
1766 if (use_theme_images(SLIDER_WIDGET))
1767 {
1768 // Slider trough
1769 SDL_Rect r = {rect.x + slider_x, rect.y + (saved_min_height - slider_l->h) / 2,
1770 static_cast<Uint16>(slider_l->w),
1771 static_cast<Uint16>(slider_l->h)};
1772 SDL_BlitSurface(slider_l, NULL, s, &r);
1773 r.x = r.x + static_cast<Sint16>(slider_l->w);
1774 r.w = static_cast<Uint16>(slider_c->w);
1775 r.h = static_cast<Uint16>(slider_c->h);
1776 SDL_BlitSurface(slider_c, NULL, s, &r);
1777 r.x = r.x + static_cast<Sint16>(slider_c->w);
1778 r.w = static_cast<Uint16>(slider_r->w);
1779 r.h = static_cast<Uint16>(slider_r->h);
1780 SDL_BlitSurface(slider_r, NULL, s, &r);
1781
1782 // Slider thumb
1783 r.x = rect.x + static_cast<Sint16>(thumb_x);
1784 r.y = rect.y + (saved_min_height - thumb->h) / 2 + get_theme_space(SLIDER_WIDGET, SLIDER_T_SPACE);
1785 r.w = static_cast<Uint16>(thumb->w);
1786 r.h = static_cast<Uint16>(thumb->h);
1787 SDL_BlitSurface(thumb, NULL, s, &r);
1788 }
1789 else
1790 {
1791 SDL_Rect r = {rect.x, rect.y + (saved_min_height - SLIDER_TROUGH_HEIGHT) / 2 + get_theme_space(SLIDER_WIDGET, SLIDER_T_SPACE), SLIDER_WIDTH, SLIDER_TROUGH_HEIGHT};
1792 uint32 pixel = get_theme_color(SLIDER_WIDGET, DEFAULT_STATE, FRAME_COLOR);
1793 draw_rectangle(s, &r, pixel);
1794
1795 pixel = get_theme_color(SLIDER_WIDGET, DEFAULT_STATE, FOREGROUND_COLOR);
1796 r.x = r.x + 1;
1797 r.y = r.y + 1;
1798 r.w = r.w - 2;
1799 r.h = r.h - 2;
1800 SDL_FillRect(s, &r, pixel);
1801
1802 pixel = get_theme_color(SLIDER_THUMB, DEFAULT_STATE, FRAME_COLOR);
1803 r.x = rect.x + static_cast<Sint16>(thumb_x);
1804 r.y = rect.y + (saved_min_height - SLIDER_THUMB_HEIGHT) / 2;
1805 r.w = thumb_width();
1806 r.h = SLIDER_THUMB_HEIGHT;
1807 draw_rectangle(s, &r, pixel);
1808
1809 pixel = get_theme_color(SLIDER_THUMB, DEFAULT_STATE, FOREGROUND_COLOR);
1810 r.x = r.x + 1;
1811 r.y = r.y + 1;
1812 r.w = r.w - 2;
1813 r.h = r.h - 2;
1814 SDL_FillRect(s, &r, pixel);
1815
1816 }
1817 readout->draw(s);
1818 }
1819
mouse_move(int x,int)1820 void w_slider::mouse_move(int x, int /*y*/)
1821 {
1822 if (thumb_dragging) {
1823 int delta_x = (x - slider_x - get_theme_space(SLIDER_WIDGET, SLIDER_L_SPACE)) - thumb_drag_x;
1824 set_selection(delta_x * num_items / (trough_width - thumb_width()));
1825 }
1826 }
1827
click(int x,int)1828 void w_slider::click(int x, int /*y*/)
1829 {
1830 if(enabled) {
1831 if (x >= thumb_x && x < thumb_x + thumb_width()) {
1832 thumb_dragging = dirty = true;
1833 thumb_drag_x = x - thumb_x;
1834 }
1835 }
1836 }
1837
event(SDL_Event & e)1838 void w_slider::event(SDL_Event &e)
1839 {
1840 if (e.type == SDL_KEYDOWN) {
1841 if (e.key.keysym.sym == SDLK_LEFT) {
1842 set_selection(selection - 1);
1843 item_selected();
1844 e.type = SDL_LASTEVENT; // Swallow event
1845 } else if (e.key.keysym.sym == SDLK_RIGHT) {
1846 set_selection(selection + 1);
1847 item_selected();
1848 e.type = SDL_LASTEVENT; // Swallow event
1849 }
1850 } else if (e.type == SDL_CONTROLLERBUTTONDOWN) {
1851 if (e.cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) {
1852 set_selection(selection - 1);
1853 item_selected();
1854 e.type = SDL_LASTEVENT; // Swallow event
1855 } else if (e.cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) {
1856 set_selection(selection + 1);
1857 item_selected();
1858 e.type = SDL_LASTEVENT; // Swallow event
1859 }
1860 } else if (e.type == SDL_MOUSEBUTTONUP) {
1861 if (thumb_dragging) {
1862 thumb_dragging = false;
1863 dirty = true;
1864 item_selected();
1865 }
1866 }
1867 }
1868
set_selection(int s)1869 void w_slider::set_selection(int s)
1870 {
1871 if (s >= num_items)
1872 s = num_items - 1;
1873 else if (s < 0)
1874 s = 0;
1875 selection = s;
1876 thumb_x = int(float(selection * (trough_width - thumb_width())) / (num_items - 1) + 0.5);
1877 thumb_x += get_theme_space(SLIDER_WIDGET, SLIDER_L_SPACE) + slider_x;
1878 dirty = true;
1879 selection_changed();
1880 }
1881
thumb_width() const1882 int w_slider::thumb_width() const
1883 {
1884 if (use_theme_images(SLIDER_WIDGET))
1885 return thumb->w;
1886 else
1887 return SLIDER_THUMB_WIDTH;
1888 }
1889
formatted_value()1890 std::string w_slider::formatted_value()
1891 {
1892 std::ostringstream ss;
1893 ss << selection;
1894 return ss.str();
1895 }
1896
selection_changed()1897 void w_slider::selection_changed()
1898 {
1899 readout->set_text(formatted_value().c_str());
1900
1901 if (slider_changed_callback != NULL)
1902 slider_changed_callback(this);
1903 }
1904
init_formatted_value()1905 void w_slider::init_formatted_value()
1906 {
1907 int temp = selection;
1908 selection = num_items;
1909 std::string tempstr = formatted_value();
1910 selection = temp;
1911 delete readout;
1912 readout = new w_slider_text(tempstr.c_str());
1913 readout->associated_slider = this;
1914 saved_min_height = std::max(saved_min_height, readout->min_height());
1915 saved_min_width = readout_x + readout->min_width();
1916 }
1917
formatted_value()1918 std::string w_percentage_slider::formatted_value()
1919 {
1920 std::ostringstream ss;
1921 ss << (selection * 100 / (num_items - 1)) << "%";
1922 return ss.str();
1923 }
1924
1925
1926 /*
1927 * List selection
1928 */
1929
w_list_base(uint16 width,size_t lines,size_t)1930 w_list_base::w_list_base(uint16 width, size_t lines, size_t /*sel*/) : widget(ITEM_WIDGET), num_items(0), shown_items(lines), thumb_dragging(false), top_item(0)
1931 {
1932 rect.w = width;
1933 rect.h = item_height() * static_cast<uint16>(shown_items) + get_theme_space(LIST_WIDGET, T_SPACE) + get_theme_space(LIST_WIDGET, B_SPACE);
1934
1935 frame_tl = get_theme_image(LIST_WIDGET, DEFAULT_STATE, TL_IMAGE);
1936 frame_tr = get_theme_image(LIST_WIDGET, DEFAULT_STATE, TR_IMAGE);
1937 frame_bl = get_theme_image(LIST_WIDGET, DEFAULT_STATE, BL_IMAGE);
1938 frame_br = get_theme_image(LIST_WIDGET, DEFAULT_STATE, BR_IMAGE);
1939 frame_t = get_theme_image(LIST_WIDGET, DEFAULT_STATE, T_IMAGE, rect.w - frame_tl->w - frame_tr->w, 0);
1940 frame_l = get_theme_image(LIST_WIDGET, DEFAULT_STATE, L_IMAGE, 0, rect.h - frame_tl->h - frame_bl->h);
1941 frame_r = get_theme_image(LIST_WIDGET, DEFAULT_STATE, R_IMAGE, 0, rect.h - frame_tr->h - frame_br->h);
1942 frame_b = get_theme_image(LIST_WIDGET, DEFAULT_STATE, B_IMAGE, rect.w - frame_bl->w - frame_br->w, 0);
1943
1944 thumb_t = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_T_IMAGE);
1945 thumb_tc = NULL;
1946 SDL_Surface *thumb_tc_unscaled = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_TC_IMAGE);
1947 thumb_c = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_C_IMAGE);
1948 SDL_Surface *thumb_bc_unscaled = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_BC_IMAGE);
1949 thumb_bc = NULL;
1950 thumb_b = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_B_IMAGE);
1951
1952 min_thumb_height = static_cast<uint16>(thumb_t->h + thumb_tc_unscaled->h + thumb_c->h + thumb_bc_unscaled->h + thumb_b->h);
1953
1954 trough_rect.x = rect.w - get_theme_space(LIST_WIDGET, TROUGH_R_SPACE);
1955 trough_rect.y = get_theme_space(LIST_WIDGET, TROUGH_T_SPACE);
1956 trough_rect.w = get_theme_space(LIST_WIDGET, TROUGH_WIDTH);
1957 trough_rect.h = rect.h - get_theme_space(LIST_WIDGET, TROUGH_T_SPACE) - get_theme_space(LIST_WIDGET, TROUGH_B_SPACE);
1958
1959 saved_min_width = rect.w;
1960 saved_min_height = rect.h;
1961 }
1962
~w_list_base()1963 w_list_base::~w_list_base()
1964 {
1965 if (frame_t) SDL_FreeSurface(frame_t);
1966 if (frame_l) SDL_FreeSurface(frame_l);
1967 if (frame_r) SDL_FreeSurface(frame_r);
1968 if (frame_b) SDL_FreeSurface(frame_b);
1969 if (thumb_tc) SDL_FreeSurface(thumb_tc);
1970 if (thumb_bc) SDL_FreeSurface(thumb_bc);
1971 }
1972
draw_image(SDL_Surface * dst,SDL_Surface * s,int16 x,int16 y) const1973 void w_list_base::draw_image(SDL_Surface *dst, SDL_Surface *s, int16 x, int16 y) const
1974 {
1975 SDL_Rect r = {x, y, static_cast<Uint16>(s->w), static_cast<Uint16>(s->h)};
1976 SDL_BlitSurface(s, NULL, dst, &r);
1977 }
1978
draw(SDL_Surface * s) const1979 void w_list_base::draw(SDL_Surface *s) const
1980 {
1981 if (use_theme_images(LIST_WIDGET))
1982 {
1983 // Draw frame
1984 int16 x = rect.x;
1985 int16 y = rect.y;
1986 draw_image(s, frame_tl, x, y);
1987 draw_image(s, frame_t, x + static_cast<int16>(frame_tl->w), y);
1988 draw_image(s, frame_tr, x + static_cast<int16>(frame_tl->w) + static_cast<int16>(frame_t->w), y);
1989 draw_image(s, frame_l, x, y + static_cast<int16>(frame_tl->h));
1990 draw_image(s, frame_r, x + rect.w - static_cast<int16>(frame_r->w), y + static_cast<int16>(frame_tr->h));
1991 draw_image(s, frame_bl, x, y + static_cast<int16>(frame_tl->h) + static_cast<int16>(frame_l->h));
1992 draw_image(s, frame_b, x + static_cast<int16>(frame_bl->w), y + rect.h - static_cast<int16>(frame_b->h));
1993 draw_image(s, frame_br, x + static_cast<int16>(frame_bl->w) + static_cast<int16>(frame_b->w), y + static_cast<int16>(frame_tr->h) + static_cast<int16>(frame_r->h));
1994
1995 // Draw thumb
1996 x = rect.x + trough_rect.x;
1997 y = rect.y + thumb_y;
1998 draw_image(s, thumb_t, x, y);
1999 draw_image(s, thumb_tc, x, y = y + static_cast<int16>(thumb_t->h));
2000 draw_image(s, thumb_c, x, y = y + static_cast<int16>(thumb_tc->h));
2001 draw_image(s, thumb_bc, x, y = y + static_cast<int16>(thumb_c->h));
2002 draw_image(s, thumb_b, x, y = y + static_cast<int16>(thumb_bc->h));
2003
2004 }
2005 else
2006 {
2007 uint32 pixel = get_theme_color(LIST_WIDGET, DEFAULT_STATE, FRAME_COLOR);
2008 draw_rectangle(s, &rect, pixel);
2009
2010 SDL_Rect real_trough = { rect.x + trough_rect.x, rect.y + trough_rect.y, trough_rect.w, trough_rect.h };
2011 draw_rectangle(s, &real_trough, pixel);
2012 real_trough.x = real_trough.x + 1;
2013 real_trough.y = real_trough.y + 1;
2014 real_trough.w = real_trough.w - 2;
2015 real_trough.h = real_trough.h - 2;
2016 if (use_theme_color(LIST_THUMB, BACKGROUND_COLOR))
2017 {
2018 pixel = get_theme_color(LIST_THUMB, DEFAULT_STATE, BACKGROUND_COLOR);
2019 SDL_FillRect(s, &real_trough, pixel);
2020 }
2021
2022 pixel = get_theme_color(LIST_THUMB, DEFAULT_STATE, FRAME_COLOR);
2023 SDL_Rect thumb_rect = { rect.x + trough_rect.x, rect.y + thumb_y, trough_rect.w, thumb_t->h + thumb_tc->h + thumb_c->h + thumb_bc->h + thumb_b->h};
2024 draw_rectangle(s, &thumb_rect, pixel);
2025
2026 pixel = get_theme_color(LIST_THUMB, DEFAULT_STATE, FOREGROUND_COLOR);
2027 thumb_rect.x = thumb_rect.x + 1;
2028 thumb_rect.y = thumb_rect.y + 1;
2029 thumb_rect.w = thumb_rect.w - 2;
2030 thumb_rect.h = thumb_rect.h - 2;
2031 SDL_FillRect(s, &thumb_rect, pixel);
2032
2033 }
2034
2035 // Draw items
2036 draw_items(s);
2037 }
2038
mouse_move(int x,int y)2039 void w_list_base::mouse_move(int x, int y)
2040 {
2041 if (thumb_dragging) {
2042 int delta_y = y - thumb_drag_y;
2043 if (delta_y > 0 && num_items > shown_items && trough_rect.h > thumb_height) {
2044 set_top_item(delta_y * (num_items - shown_items) / (trough_rect.h - thumb_height));
2045 } else {
2046 set_top_item(0);
2047 }
2048 } else {
2049 if (x < get_theme_space(LIST_WIDGET, L_SPACE) || x >= rect.w - get_theme_space(LIST_WIDGET, R_SPACE)
2050 || y < get_theme_space(LIST_WIDGET, T_SPACE) || y >= rect.h - get_theme_space(LIST_WIDGET, B_SPACE))
2051 return;
2052
2053 if ((y - get_theme_space(LIST_WIDGET, T_SPACE)) / item_height() + top_item < std::min(num_items, top_item + shown_items))
2054 { set_selection((y - get_theme_space(LIST_WIDGET, T_SPACE)) / item_height() + top_item); }
2055 // else
2056 // { set_selection(num_items - 1); }
2057 }
2058 }
2059
place(const SDL_Rect & r,placement_flags flags)2060 void w_list_base::place(const SDL_Rect& r, placement_flags flags)
2061 {
2062 widget::place(r, flags);
2063
2064 trough_rect.x = rect.w - get_theme_space(LIST_WIDGET, TROUGH_R_SPACE);
2065 trough_rect.y = get_theme_space(LIST_WIDGET, TROUGH_T_SPACE);
2066 trough_rect.w = get_theme_space(LIST_WIDGET, TROUGH_WIDTH);
2067 trough_rect.h = rect.h - get_theme_space(LIST_WIDGET, TROUGH_T_SPACE) - get_theme_space(LIST_WIDGET, TROUGH_B_SPACE);
2068
2069 frame_t = get_theme_image(LIST_WIDGET, DEFAULT_STATE, T_IMAGE, rect.w - frame_tl->w - frame_tr->w, 0);
2070 frame_l = get_theme_image(LIST_WIDGET, DEFAULT_STATE, L_IMAGE, 0, rect.h - frame_tl->h - frame_bl->h);
2071 frame_r = get_theme_image(LIST_WIDGET, DEFAULT_STATE, R_IMAGE, 0, rect.h - frame_tr->h - frame_br->h);
2072 frame_b = get_theme_image(LIST_WIDGET, DEFAULT_STATE, B_IMAGE, rect.w - frame_bl->w - frame_br->w, 0);
2073 }
2074
click(int x,int y)2075 void w_list_base::click(int x, int y)
2076 {
2077 if (x >= trough_rect.x && x < trough_rect.x + trough_rect.w
2078 // && y >= trough_rect.y && y < trough_rect.y + trough_rect.h) {
2079 && y >= thumb_y && y <= thumb_y + thumb_height) {
2080 thumb_dragging = dirty = true;
2081 thumb_drag_y = y - thumb_y;
2082 } else {
2083 if (num_items > 0 && is_item_selectable(selection))
2084 item_selected();
2085 }
2086 }
2087
event(SDL_Event & e)2088 void w_list_base::event(SDL_Event &e)
2089 {
2090 if (e.type == SDL_KEYDOWN) {
2091 switch (e.key.keysym.sym) {
2092 case SDLK_UP:
2093 if (selection != 0)
2094 { set_selection(selection - 1); }
2095 e.type = SDL_LASTEVENT; // Prevent selection of previous widget
2096 break;
2097 case SDLK_DOWN:
2098 if (selection < num_items - 1)
2099 { set_selection(selection + 1); }
2100 e.type = SDL_LASTEVENT; // Prevent selection of next widget
2101 break;
2102 case SDLK_PAGEUP:
2103 if (selection > shown_items)
2104 { set_selection(selection - shown_items); }
2105 else
2106 { set_selection(0); }
2107 break;
2108 case SDLK_PAGEDOWN:
2109 if (selection + shown_items < num_items - 1)
2110 { set_selection(selection + shown_items); }
2111 else
2112 { set_selection(num_items - 1); }
2113 break;
2114 case SDLK_HOME:
2115 set_selection(0);
2116 break;
2117 case SDLK_END:
2118 set_selection(num_items - 1);
2119 break;
2120 default:
2121 break;
2122 }
2123 } else if (e.type == SDL_CONTROLLERBUTTONDOWN) {
2124 switch (e.cbutton.button) {
2125 case SDL_CONTROLLER_BUTTON_DPAD_UP:
2126 if (selection != 0)
2127 { set_selection(selection - 1); }
2128 e.type = SDL_LASTEVENT; // Prevent selection of previous widget
2129 break;
2130 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
2131 if (selection < num_items - 1)
2132 { set_selection(selection + 1); }
2133 e.type = SDL_LASTEVENT; // Prevent selection of next widget
2134 break;
2135 }
2136 } else if (e.type == SDL_MOUSEBUTTONUP) {
2137 if (thumb_dragging) {
2138 thumb_dragging = false;
2139 dirty = true;
2140 }
2141 } else if (e.type == SDL_MOUSEWHEEL) {
2142 int amt = e.wheel.y * -1 * kListScrollSpeed;
2143 if (amt < 0) {
2144 amt = amt * -1;
2145 if (top_item > amt)
2146 set_top_item(top_item - amt);
2147 else
2148 set_top_item(0);
2149 } else if (amt > 0) {
2150 if (top_item + amt < num_items - shown_items)
2151 set_top_item(top_item + amt);
2152 else
2153 set_top_item(num_items - shown_items);
2154 }
2155 }
2156 }
2157
set_selection(size_t s)2158 void w_list_base::set_selection(size_t s)
2159 {
2160 // Set selection, check for bounds
2161 assert(s == PIN(s, 0, num_items - 1));
2162 if (s != selection)
2163 dirty = true;
2164 selection = s;
2165
2166 // Make selection visible
2167 if (s < top_item)
2168 set_top_item(s);
2169 else if (s >= top_item + shown_items)
2170 set_top_item(s - shown_items + 1);
2171 }
2172
new_items(void)2173 void w_list_base::new_items(void)
2174 {
2175 // Reset top item and selection
2176
2177 // ghs: actually, remember top item and selection
2178 size_t saved_top_item = top_item;
2179 size_t saved_selection = get_selection();
2180 top_item = selection = 0;
2181 dirty = true;
2182
2183 // Calculate thumb height
2184 if (num_items <= shown_items)
2185 thumb_height = trough_rect.h;
2186 else if (num_items == 0)
2187 thumb_height = static_cast<uint16>(shown_items) * trough_rect.h;
2188 else
2189 thumb_height = uint16(float(shown_items * trough_rect.h) / num_items + 0.5);
2190 if (thumb_height < min_thumb_height)
2191 thumb_height = min_thumb_height;
2192 else if (thumb_height > trough_rect.h)
2193 thumb_height = trough_rect.h;
2194
2195 // Create dynamic thumb images
2196 if (thumb_tc)
2197 SDL_FreeSurface(thumb_tc);
2198 if (thumb_bc)
2199 SDL_FreeSurface(thumb_bc);
2200 int rem_height = thumb_height - thumb_t->h - thumb_c->h - thumb_b->h;
2201 int dyn_height = rem_height / 2;
2202 thumb_tc = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_TC_IMAGE, 0, dyn_height);
2203 thumb_bc = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_BC_IMAGE, 0, (rem_height & 1) ? dyn_height + 1 : dyn_height);
2204
2205 thumb_y = 0;
2206 if (thumb_y > trough_rect.h - thumb_height)
2207 thumb_y = trough_rect.h - thumb_height;
2208 thumb_y = thumb_y + trough_rect.y;
2209
2210 if (saved_selection < num_items)
2211 set_selection(saved_selection);
2212 if (saved_top_item)
2213 set_top_item(saved_top_item);
2214 }
2215
center_item(size_t i)2216 void w_list_base::center_item(size_t i)
2217 {
2218 set_top_item((i > shown_items / 2) ? i - shown_items / 2 : 0);
2219 }
2220
set_top_item(size_t i)2221 void w_list_base::set_top_item(size_t i)
2222 {
2223 // Set top item (check for bounds)
2224 if (num_items > shown_items)
2225 i = PIN(i, 0, num_items - shown_items);
2226 else
2227 i = 0;
2228 if (i != top_item)
2229 dirty = true;
2230 top_item = i;
2231
2232 // Calculate thumb y position
2233 if (num_items <= shown_items)
2234 thumb_y = 0;
2235 else
2236 thumb_y = int16(float(top_item * (trough_rect.h - thumb_height)) / (num_items - shown_items) + 0.5);
2237 if (thumb_y > trough_rect.h - thumb_height)
2238 thumb_y = trough_rect.h - thumb_height;
2239 thumb_y = thumb_y + trough_rect.y;
2240 }
2241
2242
2243
2244 // ZZZ: maybe this belongs in a new file or something - it's definitely A1-related
2245 // whereas most (but not all) of the other widgets here are sort of 'general-purpose'.
2246 // Anyway, moved here from shell_sdl.h and enhanced ever so slightly, will now be
2247 // using it in the setup network game dialog as well.
2248
2249 /*
2250 * Level number dialog
2251 */
2252
w_levels(const vector<entry_point> & items,dialog * d)2253 w_levels::w_levels(const vector<entry_point> &items, dialog *d)
2254 : w_list<entry_point>(items, 400, 8, 0), parent(d), show_level_numbers(true) {}
2255
2256 // ZZZ: new constructor gives more control over widget's appearance.
w_levels(const vector<entry_point> & items,dialog * d,uint16 inWidth,size_t inNumLines,size_t inSelectedItem,bool in_show_level_numbers)2257 w_levels::w_levels(const vector<entry_point>& items, dialog* d, uint16 inWidth,
2258 size_t inNumLines, size_t inSelectedItem, bool in_show_level_numbers)
2259 : w_list<entry_point>(items, inWidth, inNumLines, inSelectedItem), parent(d), show_level_numbers(in_show_level_numbers) {}
2260
2261 void
item_selected(void)2262 w_levels::item_selected(void)
2263 {
2264 parent->quit(0);
2265 }
2266
2267 void
draw_item(vector<entry_point>::const_iterator i,SDL_Surface * s,int16 x,int16 y,uint16 width,bool selected) const2268 w_levels::draw_item(vector<entry_point>::const_iterator i, SDL_Surface *s, int16 x, int16 y, uint16 width, bool selected) const
2269 {
2270 y = y + font->get_ascent();
2271 char str[256];
2272
2273 if(show_level_numbers)
2274 sprintf(str, "%d - %s", i->level_number + 1, i->level_name);
2275 else
2276 sprintf(str, "%s", i->level_name);
2277
2278 set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width);
2279 draw_text(s, str, x, y, get_theme_color(ITEM_WIDGET, selected ? ACTIVE_STATE : DEFAULT_STATE), font, style);
2280 set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
2281 }
2282
2283
2284 /*
2285 * String List
2286 */
2287
w_string_list(const vector<string> & items,dialog * d,int sel)2288 w_string_list::w_string_list(const vector<string> &items, dialog *d, int sel)
2289 : w_list<string>(items, 400, 8, sel), parent(d) {}
2290
item_selected(void)2291 void w_string_list::item_selected(void)
2292 {
2293 parent->quit(0);
2294 }
2295
draw_item(vector<string>::const_iterator i,SDL_Surface * s,int16 x,int16 y,uint16 width,bool selected) const2296 void w_string_list::draw_item(vector<string>::const_iterator i, SDL_Surface *s, int16 x, int16 y, uint16 width, bool selected) const
2297 {
2298 y = y + font->get_ascent();
2299 char str[256];
2300
2301 sprintf(str, "%s", i->c_str ());
2302
2303 set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width);
2304 draw_text(s, str, x, y, get_theme_color(ITEM_WIDGET, selected ? ACTIVE_STATE : DEFAULT_STATE), font, style);
2305 set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
2306 }
2307
2308
2309 /*
2310 * Selection Popup
2311 */
2312
w_select_popup(action_proc p,void * a)2313 w_select_popup::w_select_popup (action_proc p, void *a) : w_select_button ("", gotSelectedCallback, NULL)
2314 {
2315 set_arg(this);
2316 selection = -1;
2317
2318 action = p;
2319 arg = a;
2320 }
2321
set_labels(const vector<string> & inLabels)2322 void w_select_popup::set_labels(const vector<string>& inLabels)
2323 {
2324 labels = inLabels;
2325
2326 // recalculate min width
2327 saved_min_width = 0;
2328 for (vector<string>::iterator it = labels.begin(); it != labels.end(); ++it)
2329 {
2330 uint16 width = text_width(it->c_str(), font, style);
2331 if (width > saved_min_width)
2332 saved_min_width = width;
2333 }
2334 }
2335
set_selection(int value)2336 void w_select_popup::set_selection (int value)
2337 {
2338 if (value < labels.size () && value >= 0)
2339 selection = value;
2340 else
2341 selection = -1;
2342
2343 if (selection == -1)
2344 w_select_button::set_selection ("");
2345 else
2346 w_select_button::set_selection (labels[selection].c_str ());
2347 }
2348
gotSelected()2349 void w_select_popup::gotSelected ()
2350 {
2351 if (labels.size () > 1) {
2352 dialog theDialog;
2353 vertical_placer *placer = new vertical_placer;
2354
2355 w_string_list* string_list_w = new w_string_list (labels, &theDialog, selection >= 0 ? selection : 0);
2356 placer->dual_add (string_list_w, theDialog);
2357 theDialog.activate_widget(string_list_w);
2358
2359 theDialog.set_widget_placer(placer);
2360 if (theDialog.run () == 0)
2361 set_selection (string_list_w->get_selection ());
2362 }
2363
2364 if (action)
2365 action (arg);
2366 }
2367
2368
2369 static const char* const sFileChooserInvalidFileString = "(no valid selection)";
2370
w_file_chooser(const char * inDialogPrompt,Typecode inTypecode)2371 w_file_chooser::w_file_chooser(const char* inDialogPrompt, Typecode inTypecode)
2372 : w_select_button("", NULL, NULL, true), typecode(inTypecode)
2373 {
2374 strncpy(dialog_prompt, inDialogPrompt, sizeof(dialog_prompt));
2375 set_selection(sFileChooserInvalidFileString);
2376 }
2377
2378
2379
2380 void
set_file(const FileSpecifier & inFile)2381 w_file_chooser::set_file(const FileSpecifier& inFile)
2382 {
2383 file = inFile;
2384 update_filename();
2385 }
2386
2387
2388
2389 void
click(int,int)2390 w_file_chooser::click(int, int)
2391 {
2392 if(enabled)
2393 {
2394 if(file.ReadDialog(typecode, dialog_prompt))
2395 {
2396 update_filename();
2397 if (m_callback)
2398 m_callback ();
2399 }
2400 }
2401 }
2402
2403
2404
2405 void
update_filename()2406 w_file_chooser::update_filename()
2407 {
2408 if(file.Exists())
2409 {
2410 file.GetName(filename);
2411 std::string filename_copy = filename;
2412 strcpy(filename, FileSpecifier::HideExtension(filename_copy).c_str());
2413 set_selection(filename);
2414 }
2415 else
2416 set_selection(sFileChooserInvalidFileString);
2417 }
2418
2419
w_items_in_room_get_name_of_item(GameListMessage::GameListEntry item)2420 const string w_items_in_room_get_name_of_item (GameListMessage::GameListEntry item)
2421 {
2422 return item.name ();
2423 }
2424
w_items_in_room_get_name_of_item(prospective_joiner_info item)2425 const string w_items_in_room_get_name_of_item (prospective_joiner_info item)
2426 {
2427 return std::string(item.name);
2428 }
2429
w_items_in_room_get_name_of_item(MetaserverPlayerInfo item)2430 const string w_items_in_room_get_name_of_item (MetaserverPlayerInfo item)
2431 {
2432 return item.name ();
2433 }
2434
draw_item(const GameListMessage::GameListEntry & item,SDL_Surface * s,int16 x,int16 y,uint16 width,bool selected) const2435 void w_games_in_room::draw_item(const GameListMessage::GameListEntry& item, SDL_Surface* s, int16 x, int16 y, uint16 width, bool selected) const
2436 {
2437 int game_style = style;
2438
2439 int state;
2440 if (!item.compatible())
2441 {
2442 state = item.target() ? SELECTED_INCOMPATIBLE_GAME : INCOMPATIBLE_GAME;
2443 }
2444 else if (item.running())
2445 {
2446 state = item.target() ? SELECTED_RUNNING_GAME : RUNNING_GAME;
2447 }
2448 else
2449 {
2450 state = item.target() ? SELECTED_GAME : GAME;
2451 }
2452
2453 uint32 fg;
2454 if (selected)
2455 {
2456 fg = get_theme_color(ITEM_WIDGET, ACTIVE_STATE);
2457 }
2458 else
2459 {
2460 fg = get_theme_color(METASERVER_GAMES, state, FOREGROUND_COLOR);
2461 }
2462
2463 uint32 bg = get_theme_color(METASERVER_GAMES, state, BACKGROUND_COLOR);
2464 SDL_Rect r = { x, y, width, 3 * font->get_line_height() + 2};
2465 SDL_FillRect(s, &r, bg);
2466
2467 if (use_theme_color(METASERVER_GAMES, FRAME_COLOR))
2468 {
2469 uint32 frame = get_theme_color(METASERVER_GAMES, state, FRAME_COLOR);
2470 draw_rectangle(s, &r, frame);
2471 }
2472
2473
2474 x += 1;
2475 width -= 2;
2476 y += font->get_ascent() + 1;
2477
2478 std::ostringstream time_or_ping;
2479 int right_text_width = 0;
2480
2481 // first line, game name, ping or time remaining
2482 if (item.running())
2483 {
2484 if (item.m_description.m_timeLimit && !(item.m_description.m_timeLimit == INT32_MAX || item.m_description.m_timeLimit == -1))
2485 {
2486 if (item.minutes_remaining() == 1)
2487 {
2488 time_or_ping << "~1 Minute";
2489 }
2490 else
2491 {
2492 time_or_ping << item.minutes_remaining() << " Minutes";
2493 }
2494 }
2495 else
2496 {
2497 time_or_ping << "Untimed";
2498 }
2499 }
2500 else
2501 {
2502 // draw ping
2503 }
2504
2505 right_text_width = text_width(time_or_ping.str().c_str(), font, game_style);
2506
2507 // draw game name
2508 set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width - right_text_width);
2509 font->draw_styled_text(s, item.name(), item.name().size(), x, y, fg, game_style);
2510
2511 // draw remaining or ping
2512 set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width);
2513 draw_text(s, time_or_ping.str().c_str(), x + width - right_text_width, y, fg, font, game_style);
2514
2515 y += font->get_line_height();
2516
2517 std::ostringstream game_and_map;
2518
2519 if (!item.compatible())
2520 {
2521 game_and_map << "|i" << item.m_description.m_scenarioName;
2522 if (item.m_description.m_scenarioVersion != "")
2523 {
2524 game_and_map << ", Version " << item.m_description.m_scenarioVersion;
2525 }
2526 }
2527 else
2528 {
2529 game_and_map << item.game_string()
2530 << " on |i"
2531 << item.m_description.m_mapName;
2532 }
2533
2534 font->draw_styled_text(s, game_and_map.str().c_str(), game_and_map.str().size(), x, y, fg, game_style);
2535
2536 y += font->get_line_height();
2537
2538 right_text_width = font->styled_text_width(item.m_hostPlayerName, item.m_hostPlayerName.size(), game_style);
2539 set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width - right_text_width);
2540
2541 std::ostringstream game_settings;
2542 if (item.running())
2543 {
2544 if (item.m_description.m_numPlayers == 1)
2545 {
2546 game_settings << "1 Player";
2547 }
2548 else
2549 {
2550 game_settings << static_cast<uint16>(item.m_description.m_numPlayers) << " Players";
2551 }
2552 }
2553 else
2554 {
2555 game_settings << static_cast<uint16>(item.m_description.m_numPlayers)
2556 << "/"
2557 << item.m_description.m_maxPlayers
2558 << " Players";
2559 }
2560
2561 if (item.m_description.m_timeLimit && !(item.m_description.m_timeLimit == INT32_MAX || item.m_description.m_timeLimit == -1))
2562 {
2563 game_settings << ", "
2564 << item.m_description.m_timeLimit / 60 / TICKS_PER_SECOND
2565 << " Minutes";
2566 }
2567
2568 if (item.m_description.m_teamsAllowed)
2569 {
2570 game_settings << ", Teams";
2571 }
2572
2573 draw_text(s, game_settings.str().c_str(), x, y, fg, font, game_style);
2574
2575 set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width);
2576 font->draw_styled_text(s, item.m_hostPlayerName, item.m_hostPlayerName.size(), x + width - right_text_width, y, fg, game_style);
2577
2578 set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
2579 }
2580
2581
darken(uint8 component,uint8 amount)2582 static inline uint8 darken(uint8 component, uint8 amount)
2583 {
2584 return PIN((uint16) component * (255 - amount) / 255, 0, 255);
2585 }
2586
lighten(uint8 component,uint8 amount)2587 static inline uint8 lighten(uint8 component, uint8 amount)
2588 {
2589 return PIN((uint16) component + (255 - component) * amount / 255, 0, 255);
2590 }
2591
draw_item(const MetaserverPlayerInfo & item,SDL_Surface * s,int16 x,int16 y,uint16 width,bool selected) const2592 void w_players_in_room::draw_item(const MetaserverPlayerInfo& item, SDL_Surface* s,
2593 int16 x, int16 y, uint16 width, bool selected) const
2594 {
2595 set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width);
2596
2597 SDL_Rect r = {x, y, width, font->get_line_height() + 4};
2598
2599 if (item.target())
2600 {
2601 SDL_FillRect(s, &r, SDL_MapRGB(s->format, 0xff, 0xff, 0xff));
2602 }
2603
2604 // background is player color
2605 uint32 pixel;
2606 if (item.target())
2607 {
2608 int amount = 0x7f;
2609 pixel = SDL_MapRGB(s->format,
2610 lighten(item.color()[0] >> 8, amount),
2611 lighten(item.color()[1] >> 8, amount),
2612 lighten(item.color()[2] >> 8, amount));
2613 }
2614 else
2615 {
2616 int amount;
2617 if (item.away())
2618 amount = 0xbf;
2619 else
2620 amount = 0;
2621
2622 pixel = SDL_MapRGB(s->format,
2623 darken(item.color()[0] >> 8, amount),
2624 darken(item.color()[1] >> 8, amount),
2625 darken(item.color()[2] >> 8, amount));
2626 }
2627
2628 r.x = x + kPlayerColorSwatchWidth + kSwatchGutter + 1;
2629 r.y = y + 1;
2630 r.w = width - kPlayerColorSwatchWidth - kSwatchGutter - 2;
2631 r.h = font->get_line_height() + 2;
2632 SDL_FillRect(s, &r, pixel);
2633
2634 // team swatch
2635 r.x = x + 1;
2636 r.y = y + 1;
2637 r.w = kPlayerColorSwatchWidth;
2638 r.h = font->get_line_height() + 2;
2639
2640 if (item.target())
2641 {
2642 int amount = 0x7f;
2643 pixel = SDL_MapRGB(s->format,
2644 lighten(item.team_color()[0] >> 8, amount),
2645 lighten(item.team_color()[1] >> 8, amount),
2646 lighten(item.team_color()[2] >> 8, amount));
2647 }
2648 else
2649 {
2650 int amount;
2651 if (item.away())
2652 amount = 0x7f;
2653 else
2654 amount = 0;
2655
2656 pixel = SDL_MapRGB(s->format,
2657 darken(item.team_color()[0] >> 8, amount),
2658 darken(item.team_color()[1] >> 8, amount),
2659 darken(item.team_color()[2] >> 8, amount));
2660 }
2661
2662 SDL_FillRect(s, &r, pixel);
2663
2664 y += font->get_ascent();
2665 uint32 color;
2666 if (selected)
2667 {
2668 color = get_theme_color(ITEM_WIDGET, ACTIVE_STATE);
2669 }
2670 else if (item.away())
2671 {
2672 color = SDL_MapRGB(s->format, 0x7f, 0x7f, 0x7f);
2673 }
2674 else
2675 {
2676 color = SDL_MapRGB(s->format, 0xff, 0xff, 0xff);
2677 }
2678
2679 font->draw_styled_text(s, item.name(), item.name().size(), x + kPlayerColorSwatchWidth + kSwatchGutter + 2, y + 1, color, item.away() ? style : style | styleShadow);
2680
2681 set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
2682 }
2683
2684
append_entry(const ColoredChatEntry & e)2685 void w_colorful_chat::append_entry(const ColoredChatEntry& e)
2686 {
2687 if (e.message.empty())
2688 {
2689 get_owning_dialog()->draw_dirty_widgets();
2690 return;
2691 }
2692
2693 string name;
2694 if (font->styled_text_width(e.sender, e.sender.size(), style | styleShadow) > kNameWidth)
2695 name = string(e.sender, 0, font->trunc_styled_text(e.sender, kNameWidth, style | styleShadow));
2696 else
2697 name = e.sender;
2698
2699 int message_style = style;
2700 int available_width = rect.w - get_theme_space(LIST_WIDGET, L_SPACE) - get_theme_space(LIST_WIDGET, R_SPACE);
2701 if (e.type == ColoredChatEntry::ChatMessage)
2702 {
2703 available_width -= kNameWidth + taper_width() + 2;
2704 }
2705 else if (e.type == ColoredChatEntry::PrivateMessage)
2706 {
2707 message_style |= styleShadow;
2708 available_width -= kNameWidth + taper_width() + 4;
2709 }
2710 else
2711 {
2712 message_style |= styleShadow;
2713 available_width -= 2;
2714 }
2715
2716 size_t usable_characters = font->trunc_styled_text(e.message, available_width, message_style);
2717 string::const_iterator middle;
2718 string::const_iterator rest;
2719 if (usable_characters != e.message.size()) {
2720 size_t last_space = e.message.find_last_of(' ', usable_characters);
2721 if (last_space != 0 && last_space <= usable_characters)
2722 {
2723 middle = e.message.begin() + last_space;
2724 rest = middle + 1;
2725 }
2726 else
2727 {
2728 middle = e.message.begin() + usable_characters;
2729 rest = middle;
2730 }
2731 } else {
2732 middle = e.message.begin() + usable_characters;
2733 rest = middle;
2734 }
2735
2736 ColoredChatEntry e_begin = e;
2737 e_begin.message = string(e.message.begin(), middle);
2738 e_begin.sender = name;
2739
2740 ColoredChatEntry e_rest = e;
2741 if (rest != e.message.end())
2742 {
2743 e_rest.message = font->style_at(e.message, middle, message_style) + string(rest, e.message.end());
2744 }
2745 else
2746 e_rest.message = string(rest, e.message.end());
2747 e_rest.sender = name;
2748
2749 bool save_top_item = top_item < num_items - shown_items;
2750 size_t saved_top_item = top_item;
2751 entries.push_back(e_begin);
2752
2753 num_items = entries.size();
2754 new_items();
2755 if (save_top_item) {
2756 set_top_item(saved_top_item);
2757 } else if (num_items > shown_items) {
2758 set_top_item(num_items - shown_items);
2759 }
2760
2761 append_entry(e_rest);
2762 }
2763
draw_item(vector<ColoredChatEntry>::const_iterator it,SDL_Surface * s,int16 x,int16 y,uint16 width,bool selected) const2764 void w_colorful_chat::draw_item(vector<ColoredChatEntry>::const_iterator it, SDL_Surface *s, int16 x, int16 y, uint16 width, bool selected) const
2765 {
2766 int computed_y = y + font->get_ascent();
2767
2768 uint16 message_x = x;
2769 uint16 message_width = width;
2770
2771 if ((*it).type == ColoredChatEntry::ChatMessage || (*it).type == ColoredChatEntry::PrivateMessage)
2772 {
2773 // draw the name
2774 SDL_Rect r = { x, y, kNameWidth, font->get_line_height() + 1};
2775 uint32 pixel = SDL_MapRGB(s->format,
2776 (*it).color.red >> 8,
2777 (*it).color.green >> 8,
2778 (*it).color.blue >> 8);
2779 SDL_FillRect(s, &r, pixel);
2780
2781 // draw taper
2782 r.x += kNameWidth ;
2783 if (it->type == ColoredChatEntry::PrivateMessage)
2784 {
2785 // red bar under taper
2786 r.w = taper_width() + 2;
2787 SDL_FillRect(s, &r, SDL_MapRGB(s->format, 0x7f, 0x0, 0x0));
2788 }
2789
2790 r.w = 1;
2791 for (int i = 0; i < taper_width(); ++i)
2792 {
2793 r.y++;
2794 r.h -= 2;
2795 SDL_FillRect(s, &r, pixel);
2796 r.x++;
2797 }
2798
2799 set_drawing_clip_rectangle(0, x, static_cast<uint16>(s->h), x + kNameWidth);
2800 font->draw_styled_text(s, it->sender, it->sender.size(), x + 1, computed_y, SDL_MapRGB(s->format, 0xff, 0xff, 0xff), style | styleShadow);
2801
2802 message_x += kNameWidth + taper_width() + 2;
2803 message_width -= kNameWidth + taper_width() + 2;
2804 }
2805
2806 uint32 message_color = SDL_MapRGB(s->format, 0xff, 0xff, 0xff);
2807 if (it->type == ColoredChatEntry::ChatMessage)
2808 {
2809 message_color = get_theme_color(CHAT_ENTRY, DEFAULT_STATE, FOREGROUND_COLOR);
2810 }
2811
2812 int message_style = style;
2813 if ((*it).type != ColoredChatEntry::ChatMessage) message_style |= styleShadow;
2814
2815 if ((*it).type == ColoredChatEntry::ServerMessage)
2816 {
2817 // draw the blue bar
2818 uint32 pixel = SDL_MapRGB(s->format, 0x0, 0x0, 0x7f);
2819 SDL_Rect r = { message_x, y, message_width, font->get_line_height() + 1 };
2820 SDL_FillRect(s, &r, pixel);
2821 message_x += 1;
2822 message_width -= 2;
2823 }
2824 else if ((*it).type == ColoredChatEntry::PrivateMessage)
2825 {
2826 // draw a red bar
2827 uint32 pixel = SDL_MapRGB(s->format, 0x7f, 0x0, 0x0);
2828 SDL_Rect r = { message_x, y, message_width, font->get_line_height() + 1 };
2829 SDL_FillRect(s, &r, pixel);
2830 message_x += 1;
2831 message_width -= 2;
2832 }
2833 else if ((*it).type == ColoredChatEntry::LocalMessage)
2834 {
2835 // draw a gray bar
2836 uint32 pixel = SDL_MapRGB(s->format, 0x3f, 0x3f, 0x3f);
2837 SDL_Rect r = { message_x, y, message_width, font->get_line_height() + 1 };
2838 SDL_FillRect(s, &r, pixel);
2839 message_x += 1;
2840 message_width -= 2;
2841 }
2842
2843 set_drawing_clip_rectangle(0, message_x, static_cast<uint16>(s->h), message_x + message_width);
2844 font->draw_styled_text(s, it->message, it->message.size(), message_x, computed_y, message_color, message_style);
2845
2846 set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
2847 }
2848
hide()2849 void SDLWidgetWidget::hide ()
2850 {
2851 hidden = true;
2852 m_widget->set_enabled (false);
2853 }
2854
show()2855 void SDLWidgetWidget::show ()
2856 {
2857 hidden = false;
2858 m_widget->set_enabled (!inactive);
2859 }
2860
deactivate()2861 void SDLWidgetWidget::deactivate ()
2862 {
2863 inactive = true;
2864 m_widget->set_enabled (false);
2865 }
2866
activate()2867 void SDLWidgetWidget::activate ()
2868 {
2869 inactive = false;
2870 m_widget->set_enabled (!hidden);
2871 }
2872
2873
PlayersInGameWidget(w_players_in_game2 * pig)2874 PlayersInGameWidget::PlayersInGameWidget (w_players_in_game2* pig)
2875 : SDLWidgetWidget (pig)
2876 , m_pig (pig)
2877 {}
2878
redraw()2879 void PlayersInGameWidget::redraw ()
2880 {
2881 m_pig->start_displaying_actual_information ();
2882 m_pig->update_display ();
2883 m_pig->get_owning_dialog ()->draw_dirty_widgets ();
2884 }
2885
2886
2887
2888