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