1 /*
2 * Copyright (c) 2002-2007 Hypertriton, Inc. <http://hypertriton.com/>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
23 * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include <agar/core/core.h>
27 #include <agar/gui/ucombo.h>
28 #include <agar/gui/primitive.h>
29
30 AG_UCombo *
AG_UComboNew(void * parent,Uint flags)31 AG_UComboNew(void *parent, Uint flags)
32 {
33 AG_UCombo *com;
34
35 com = Malloc(sizeof(AG_UCombo));
36 AG_ObjectInit(com, &agUComboClass);
37 com->flags |= flags;
38
39 if (flags & AG_UCOMBO_HFILL) { AG_ExpandHoriz(com); }
40 if (flags & AG_UCOMBO_VFILL) { AG_ExpandVert(com); }
41
42 if (flags & AG_UCOMBO_SCROLLTOSEL)
43 com->list->flags |= AG_TLIST_SCROLLTOSEL;
44
45 AG_ObjectAttach(parent, com);
46 return (com);
47 }
48
49 AG_UCombo *
AG_UComboNewPolled(void * parent,Uint flags,AG_EventFn fn,const char * fmt,...)50 AG_UComboNewPolled(void *parent, Uint flags, AG_EventFn fn, const char *fmt,
51 ...)
52 {
53 AG_UCombo *com;
54 AG_Event *ev;
55
56 com = AG_UComboNew(parent, flags);
57 AG_ObjectLock(com);
58 com->list->flags |= AG_TLIST_POLL;
59 ev = AG_SetEvent(com->list, "tlist-poll", fn, NULL);
60 AG_EVENT_GET_ARGS(ev, fmt);
61 AG_ObjectUnlock(com);
62 return (com);
63 }
64
65 /* The UCombo must be locked. */
66 static void
Collapse(AG_UCombo * com)67 Collapse(AG_UCombo *com)
68 {
69 if (com->panel == NULL) {
70 return;
71 }
72 com->wSaved = WIDTH(com->panel);
73 com->hSaved = HEIGHT(com->panel);
74
75 AG_ObjectDetach(com->list);
76 AG_ObjectDetach(com->panel);
77 com->panel = NULL;
78
79 AG_SetInt(com->button, "state", 0);
80 }
81
82 static void
ModalClose(AG_Event * event)83 ModalClose(AG_Event *event)
84 {
85 AG_UCombo *com = AG_PTR(1);
86
87 if (com->panel != NULL)
88 Collapse(com);
89 }
90
91 static void
Expand(AG_Event * event)92 Expand(AG_Event *event)
93 {
94 AG_UCombo *com = AG_PTR(1);
95 AG_Driver *drv = WIDGET(com)->drv;
96 int expand = AG_INT(2);
97 AG_SizeReq rList;
98 int x, y, w, h;
99 Uint wView, hView;
100
101 AG_ObjectLock(com);
102 if (expand) {
103 com->panel = AG_WindowNew(
104 AG_WINDOW_NOTITLE|AG_WINDOW_DENYFOCUS|AG_WINDOW_KEEPABOVE|
105 AG_WINDOW_MODAL);
106 com->panel->wmType = AG_WINDOW_WM_COMBO;
107 AG_ObjectSetName(com->panel, "_UComboPopup");
108 AG_WindowSetPadding(com->panel, 0,0,0,0);
109 AG_ObjectAttach(com->panel, com->list);
110 if (WIDGET(com)->window != NULL) {
111 AG_WindowAttach(WIDGET(com)->window, com->panel);
112 AG_WindowMakeTransient(WIDGET(com)->window, com->panel);
113 AG_WindowPin(WIDGET(com)->window, com->panel);
114 }
115
116 if (com->wSaved > 0) {
117 w = com->wSaved;
118 h = com->hSaved;
119 } else {
120 if (com->wPreList != -1 && com->hPreList != -1) {
121 AG_TlistSizeHintPixels(com->list,
122 com->wPreList, com->hPreList);
123 }
124 AG_WidgetSizeReq(com->list, &rList);
125 w = rList.w + com->panel->wBorderSide*2;
126 h = rList.h + com->panel->wBorderBot;
127 }
128 x = WIDGET(com)->rView.x2 - w;
129 y = WIDGET(com)->rView.y1;
130
131 AG_GetDisplaySize(WIDGET(com)->drv, &wView, &hView);
132 if (x+w > wView) { w = wView - x; }
133 if (y+h > hView) { h = hView - y; }
134
135 if (AGDRIVER_CLASS(drv)->wm == AG_WM_MULTIPLE &&
136 WIDGET(com)->window != NULL) {
137 x += WIDGET(WIDGET(com)->window)->x;
138 y += WIDGET(WIDGET(com)->window)->y;
139 }
140 if (x < 0) { x = 0; }
141 if (y < 0) { y = 0; }
142 if (w < 4 || h < 4) {
143 Collapse(com);
144 return;
145 }
146 AG_SetEvent(com->panel, "window-modal-close",
147 ModalClose, "%p", com);
148 AG_WindowSetGeometry(com->panel, x,y, w,h);
149 AG_WindowShow(com->panel);
150 } else {
151 Collapse(com);
152 }
153 AG_ObjectUnlock(com);
154 }
155
156 static void
SelectedItem(AG_Event * event)157 SelectedItem(AG_Event *event)
158 {
159 AG_Tlist *tl = AG_SELF();
160 AG_UCombo *com = AG_PTR(1);
161 AG_TlistItem *it;
162
163 AG_ObjectLock(com);
164 AG_ObjectLock(tl);
165 if ((it = AG_TlistSelectedItem(tl)) != NULL) {
166 it->selected++;
167 AG_ButtonTextS(com->button, it->text);
168 AG_PostEvent(NULL, com, "ucombo-selected", "%p", it);
169 }
170 Collapse(com);
171 AG_ObjectUnlock(tl);
172 AG_ObjectUnlock(com);
173 }
174
175 static void
OnDetach(AG_Event * event)176 OnDetach(AG_Event *event)
177 {
178 AG_UCombo *com = AG_SELF();
179
180 if (com->panel != NULL) {
181 AG_ObjectDetach(com->list);
182 AG_ObjectDetach(com->panel);
183 com->panel = NULL;
184 }
185 }
186
187 static void
Init(void * obj)188 Init(void *obj)
189 {
190 AG_UCombo *com = obj;
191
192 WIDGET(com)->flags |= AG_WIDGET_UNFOCUSED_BUTTONUP|
193 AG_WIDGET_TABLE_EMBEDDABLE;
194
195 com->flags = 0;
196 com->panel = NULL;
197 com->wSaved = 0;
198 com->hSaved = 0;
199 com->wPreList = -1;
200 com->hPreList = -1;
201
202 com->button = AG_ButtonNewS(com, AG_BUTTON_STICKY, _("..."));
203 AG_ButtonSetPadding(com->button, 0,0,0,0);
204 AG_LabelSetPadding(com->button->lbl, 0,0,0,0);
205 AG_WidgetSetFocusable(com->button, 0);
206
207 com->list = Malloc(sizeof(AG_Tlist));
208 AG_ObjectInit(com->list, &agTlistClass);
209 AG_Expand(com->list);
210 AG_WidgetForwardFocus(com, com->button);
211
212 AG_SetEvent(com, "detached", OnDetach, NULL);
213 AG_SetEvent(com->button, "button-pushed", Expand, "%p", com);
214 AG_SetEvent(com->list, "tlist-changed", SelectedItem, "%p", com);
215 }
216
217 void
AG_UComboSizeHint(AG_UCombo * com,const char * text,int h)218 AG_UComboSizeHint(AG_UCombo *com, const char *text, int h)
219 {
220 AG_ObjectLock(com);
221 AG_TextSize(text, &com->wPreList, NULL);
222 com->hPreList = h;
223 AG_ObjectUnlock(com);
224 }
225
226 void
AG_UComboSizeHintPixels(AG_UCombo * com,int w,int h)227 AG_UComboSizeHintPixels(AG_UCombo *com, int w, int h)
228 {
229 AG_ObjectLock(com);
230 com->wPreList = w;
231 com->hPreList = h;
232 AG_ObjectUnlock(com);
233 }
234
235 static void
Destroy(void * p)236 Destroy(void *p)
237 {
238 AG_UCombo *com = p;
239
240 AG_ObjectDestroy(com->list);
241 }
242
243 static void
Draw(void * obj)244 Draw(void *obj)
245 {
246 AG_UCombo *com = obj;
247
248 AG_WidgetDraw(com->button);
249 }
250
251 static void
SizeRequest(void * obj,AG_SizeReq * r)252 SizeRequest(void *obj, AG_SizeReq *r)
253 {
254 AG_UCombo *com = obj;
255 AG_SizeReq rButton;
256
257 AG_WidgetSizeReq(com->button, &rButton);
258 r->w = rButton.w;
259 r->h = rButton.h;
260 }
261
262 static int
SizeAllocate(void * obj,const AG_SizeAlloc * a)263 SizeAllocate(void *obj, const AG_SizeAlloc *a)
264 {
265 AG_UCombo *com = obj;
266 AG_SizeAlloc aButton;
267
268 aButton.x = 0;
269 aButton.y = 0;
270 aButton.w = a->w;
271 aButton.h = a->h;
272 AG_WidgetSizeAlloc(com->button, &aButton);
273 return (0);
274 }
275
276 AG_WidgetClass agUComboClass = {
277 {
278 "Agar(Widget:UCombo)",
279 sizeof(AG_UCombo),
280 { 0,0 },
281 Init,
282 NULL, /* free */
283 Destroy,
284 NULL, /* load */
285 NULL, /* save */
286 NULL /* edit */
287 },
288 Draw,
289 SizeRequest,
290 SizeAllocate
291 };
292