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