1 //
2 // Copyright(C) 2005-2014 Simon Howard
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14
15 #include <stdlib.h>
16 #include <string.h>
17
18 #include "doomkeys.h"
19
20 #include "txt_button.h"
21 #include "txt_dropdown.h"
22 #include "txt_gui.h"
23 #include "txt_io.h"
24 #include "txt_main.h"
25 #include "txt_utf8.h"
26 #include "txt_window.h"
27
28 typedef struct
29 {
30 txt_window_t *window;
31 txt_dropdown_list_t *list;
32 int item;
33 } callback_data_t;
34
35 // Check if the selected value for a list is valid
36
ValidSelection(txt_dropdown_list_t * list)37 static int ValidSelection(txt_dropdown_list_t *list)
38 {
39 return *list->variable >= 0 && *list->variable < list->num_values;
40 }
41
42 // Calculate the Y position for the selector window
43
SelectorWindowY(txt_dropdown_list_t * list)44 static int SelectorWindowY(txt_dropdown_list_t *list)
45 {
46 int result;
47
48 if (ValidSelection(list))
49 {
50 result = list->widget.y - 1 - *list->variable;
51 }
52 else
53 {
54 result = list->widget.y - 1 - (list->num_values / 2);
55 }
56
57 // Keep dropdown inside the screen.
58
59 if (result < 1)
60 {
61 result = 1;
62 }
63 else if (result + list->num_values > (TXT_SCREEN_H - 3))
64 {
65 result = TXT_SCREEN_H - list->num_values - 3;
66 }
67
68 return result;
69 }
70
71 // Called when a button in the selector window is pressed
72
ItemSelected(TXT_UNCAST_ARG (button),TXT_UNCAST_ARG (callback_data))73 static void ItemSelected(TXT_UNCAST_ARG(button), TXT_UNCAST_ARG(callback_data))
74 {
75 TXT_CAST_ARG(callback_data_t, callback_data);
76
77 // Set the variable
78
79 *callback_data->list->variable = callback_data->item;
80
81 TXT_EmitSignal(callback_data->list, "changed");
82
83 // Close the window
84
85 TXT_CloseWindow(callback_data->window);
86 }
87
88 // Free callback data when the window is closed
89
FreeCallbackData(TXT_UNCAST_ARG (list),TXT_UNCAST_ARG (callback_data))90 static void FreeCallbackData(TXT_UNCAST_ARG(list),
91 TXT_UNCAST_ARG(callback_data))
92 {
93 TXT_CAST_ARG(callback_data_t, callback_data);
94
95 free(callback_data);
96 }
97
98 // Catch presses of escape and close the window.
99
SelectorWindowListener(txt_window_t * window,int key,void * user_data)100 static int SelectorWindowListener(txt_window_t *window, int key, void *user_data)
101 {
102 if (key == KEY_ESCAPE)
103 {
104 TXT_CloseWindow(window);
105 return 1;
106 }
107
108 return 0;
109 }
110
SelectorMouseListener(txt_window_t * window,int x,int y,int b,void * unused)111 static int SelectorMouseListener(txt_window_t *window, int x, int y, int b,
112 void *unused)
113 {
114 txt_widget_t *win;
115
116 win = (txt_widget_t *) window;
117
118 if (x < win->x || x > win->x + win->w || y < win->y || y > win->y + win->h)
119 {
120 TXT_CloseWindow(window);
121 return 1;
122 }
123
124 return 0;
125 }
126
127 // Open the dropdown list window to select an item
128
OpenSelectorWindow(txt_dropdown_list_t * list)129 static void OpenSelectorWindow(txt_dropdown_list_t *list)
130 {
131 txt_window_t *window;
132 int i;
133
134 // Open a simple window with no title bar or action buttons.
135
136 window = TXT_NewWindow(NULL);
137
138 TXT_SetWindowAction(window, TXT_HORIZ_LEFT, NULL);
139 TXT_SetWindowAction(window, TXT_HORIZ_CENTER, NULL);
140 TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, NULL);
141
142 // Position the window so that the currently selected item appears
143 // over the top of the list widget.
144
145 TXT_SetWindowPosition(window, TXT_HORIZ_LEFT, TXT_VERT_TOP,
146 list->widget.x - 2, SelectorWindowY(list));
147
148 // Add a button to the window for each option in the list.
149
150 for (i=0; i<list->num_values; ++i)
151 {
152 txt_button_t *button;
153 callback_data_t *data;
154
155 button = TXT_NewButton(list->values[i]);
156
157 TXT_AddWidget(window, button);
158
159 // Callback struct
160
161 data = malloc(sizeof(callback_data_t));
162 data->list = list;
163 data->window = window;
164 data->item = i;
165
166 // When the button is pressed, invoke the button press callback
167
168 TXT_SignalConnect(button, "pressed", ItemSelected, data);
169
170 // When the window is closed, free back the callback struct
171
172 TXT_SignalConnect(window, "closed", FreeCallbackData, data);
173
174 // Is this the currently-selected value? If so, select the button
175 // in the window as the default.
176
177 if (i == *list->variable)
178 {
179 TXT_SelectWidget(window, button);
180 }
181 }
182
183 // Catch presses of escape in this window and close it.
184
185 TXT_SetKeyListener(window, SelectorWindowListener, NULL);
186 TXT_SetMouseListener(window, SelectorMouseListener, NULL);
187 }
188
DropdownListWidth(txt_dropdown_list_t * list)189 static int DropdownListWidth(txt_dropdown_list_t *list)
190 {
191 int i;
192 int result;
193
194 // Find the maximum string width
195
196 result = 0;
197
198 for (i=0; i<list->num_values; ++i)
199 {
200 int w = TXT_UTF8_Strlen(list->values[i]);
201 if (w > result)
202 {
203 result = w;
204 }
205 }
206
207 return result;
208 }
209
TXT_DropdownListSizeCalc(TXT_UNCAST_ARG (list))210 static void TXT_DropdownListSizeCalc(TXT_UNCAST_ARG(list))
211 {
212 TXT_CAST_ARG(txt_dropdown_list_t, list);
213
214 list->widget.w = DropdownListWidth(list);
215 list->widget.h = 1;
216 }
217
TXT_DropdownListDrawer(TXT_UNCAST_ARG (list))218 static void TXT_DropdownListDrawer(TXT_UNCAST_ARG(list))
219 {
220 TXT_CAST_ARG(txt_dropdown_list_t, list);
221 unsigned int i;
222 const char *str;
223
224 // Set bg/fg text colors.
225
226 TXT_SetWidgetBG(list);
227
228 // Select a string to draw from the list, if the current value is
229 // in range. Otherwise fall back to a default.
230
231 if (ValidSelection(list))
232 {
233 str = list->values[*list->variable];
234 }
235 else
236 {
237 str = "???";
238 }
239
240 // Draw the string and fill to the end with spaces
241
242 TXT_DrawString(str);
243
244 for (i = TXT_UTF8_Strlen(str); i < list->widget.w; ++i)
245 {
246 TXT_DrawString(" ");
247 }
248 }
249
TXT_DropdownListDestructor(TXT_UNCAST_ARG (list))250 static void TXT_DropdownListDestructor(TXT_UNCAST_ARG(list))
251 {
252 }
253
TXT_DropdownListKeyPress(TXT_UNCAST_ARG (list),int key)254 static int TXT_DropdownListKeyPress(TXT_UNCAST_ARG(list), int key)
255 {
256 TXT_CAST_ARG(txt_dropdown_list_t, list);
257
258 if (key == KEY_ENTER)
259 {
260 OpenSelectorWindow(list);
261 return 1;
262 }
263
264 return 0;
265 }
266
TXT_DropdownListMousePress(TXT_UNCAST_ARG (list),int x,int y,int b)267 static void TXT_DropdownListMousePress(TXT_UNCAST_ARG(list),
268 int x, int y, int b)
269 {
270 TXT_CAST_ARG(txt_dropdown_list_t, list);
271
272 // Left mouse click does the same as selecting and pressing enter
273
274 if (b == TXT_MOUSE_LEFT)
275 {
276 TXT_DropdownListKeyPress(list, KEY_ENTER);
277 }
278 }
279
280 txt_widget_class_t txt_dropdown_list_class =
281 {
282 TXT_AlwaysSelectable,
283 TXT_DropdownListSizeCalc,
284 TXT_DropdownListDrawer,
285 TXT_DropdownListKeyPress,
286 TXT_DropdownListDestructor,
287 TXT_DropdownListMousePress,
288 NULL,
289 };
290
TXT_NewDropdownList(int * variable,const char ** values,int num_values)291 txt_dropdown_list_t *TXT_NewDropdownList(int *variable, const char **values,
292 int num_values)
293 {
294 txt_dropdown_list_t *list;
295
296 list = malloc(sizeof(txt_dropdown_list_t));
297
298 TXT_InitWidget(list, &txt_dropdown_list_class);
299 list->variable = variable;
300 list->values = values;
301 list->num_values = num_values;
302
303 return list;
304 }
305
306