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