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