xref: /haiku/src/kits/interface/PopUpMenu.cpp (revision 9ecf9d1c1d4888d341a6eac72112c72d1ae3a4cb)
1 /*
2  * Copyright 2001-2006, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Marc Flerackers (mflerackers@androme.be)
7  *		Stefano Ceccherini (burton666@libero.it)
8  */
9 
10 
11 #include <Application.h>
12 #include <Looper.h>
13 #include <MenuItem.h>
14 #include <PopUpMenu.h>
15 #include <Window.h>
16 
17 
18 struct popup_menu_data {
19 	BPopUpMenu *object;
20 	BWindow *window;
21 	BMenuItem *selected;
22 
23 	BPoint where;
24 	BRect rect;
25 
26 	bool async;
27 	bool autoInvoke;
28 	bool startOpened;
29 	bool useRect;
30 
31 	sem_id lock;
32 };
33 
34 
35 BPopUpMenu::BPopUpMenu(const char *title, bool radioMode, bool autoRename,
36 		menu_layout layout)
37 	: BMenu(title, layout),
38 	fUseWhere(false),
39 	fAutoDestruct(false),
40 	fTrackThread(-1)
41 {
42 	if (radioMode)
43 		SetRadioMode(true);
44 
45 	if (autoRename)
46 		SetLabelFromMarked(true);
47 }
48 
49 
50 BPopUpMenu::BPopUpMenu(BMessage *archive)
51 	: BMenu(archive),
52 	fUseWhere(false),
53 	fAutoDestruct(false),
54 	fTrackThread(-1)
55 {
56 }
57 
58 
59 BPopUpMenu::~BPopUpMenu()
60 {
61 	if (fTrackThread >= 0) {
62 		status_t status;
63 		while (wait_for_thread(fTrackThread, &status) == B_INTERRUPTED)
64 			;
65 	}
66 }
67 
68 
69 status_t
70 BPopUpMenu::Archive(BMessage *data, bool deep) const
71 {
72 	return BMenu::Archive(data, deep);
73 }
74 
75 
76 BArchivable *
77 BPopUpMenu::Instantiate(BMessage *data)
78 {
79 	if (validate_instantiation(data, "BPopUpMenu"))
80 		return new BPopUpMenu(data);
81 
82 	return NULL;
83 }
84 
85 
86 BMenuItem *
87 BPopUpMenu::Go(BPoint where, bool deliversMessage, bool openAnyway, bool async)
88 {
89 	return _go(where, deliversMessage, openAnyway, NULL, async);
90 }
91 
92 
93 BMenuItem *
94 BPopUpMenu::Go(BPoint where, bool deliversMessage, bool openAnyway,
95 	BRect clickToOpen, bool async)
96 {
97 	return _go(where, deliversMessage, openAnyway, &clickToOpen, async);
98 }
99 
100 
101 void
102 BPopUpMenu::MessageReceived(BMessage *msg)
103 {
104 	BMenu::MessageReceived(msg);
105 }
106 
107 
108 void
109 BPopUpMenu::MouseDown(BPoint point)
110 {
111 	BView::MouseDown(point);
112 }
113 
114 
115 void
116 BPopUpMenu::MouseUp(BPoint point)
117 {
118 	BView::MouseUp(point);
119 }
120 
121 
122 void
123 BPopUpMenu::MouseMoved(BPoint point, uint32 code, const BMessage *msg)
124 {
125 	BView::MouseMoved(point, code, msg);
126 }
127 
128 
129 void
130 BPopUpMenu::AttachedToWindow()
131 {
132 	BMenu::AttachedToWindow();
133 }
134 
135 
136 void
137 BPopUpMenu::DetachedFromWindow()
138 {
139 	BMenu::DetachedFromWindow();
140 }
141 
142 
143 void
144 BPopUpMenu::FrameMoved(BPoint newPosition)
145 {
146 	BMenu::FrameMoved(newPosition);
147 }
148 
149 
150 void
151 BPopUpMenu::FrameResized(float newWidth, float newHeight)
152 {
153 	BMenu::FrameResized(newWidth, newHeight);
154 }
155 
156 
157 BHandler *
158 BPopUpMenu::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier,
159 	int32 form, const char *property)
160 {
161 	return BMenu::ResolveSpecifier(msg, index, specifier, form, property);
162 }
163 
164 
165 status_t
166 BPopUpMenu::GetSupportedSuites(BMessage *data)
167 {
168 	return BMenu::GetSupportedSuites(data);
169 }
170 
171 
172 status_t
173 BPopUpMenu::Perform(perform_code d, void *arg)
174 {
175 	return BMenu::Perform(d, arg);
176 }
177 
178 
179 void
180 BPopUpMenu::ResizeToPreferred()
181 {
182 	BMenu::ResizeToPreferred();
183 }
184 
185 
186 void
187 BPopUpMenu::GetPreferredSize(float *_width, float *_height)
188 {
189 	BMenu::GetPreferredSize(_width, _height);
190 }
191 
192 
193 void
194 BPopUpMenu::MakeFocus(bool state)
195 {
196 	BMenu::MakeFocus(state);
197 }
198 
199 
200 void
201 BPopUpMenu::AllAttached()
202 {
203 	BMenu::AllAttached();
204 }
205 
206 
207 void
208 BPopUpMenu::AllDetached()
209 {
210 	BMenu::AllDetached();
211 }
212 
213 
214 void
215 BPopUpMenu::SetAsyncAutoDestruct(bool state)
216 {
217 	fAutoDestruct = state;
218 }
219 
220 
221 bool
222 BPopUpMenu::AsyncAutoDestruct() const
223 {
224 	return fAutoDestruct;
225 }
226 
227 
228 BPoint
229 BPopUpMenu::ScreenLocation()
230 {
231 	if (fUseWhere)
232 		return fWhere;
233 
234 	BMenuItem *superItem = Superitem();
235 	BMenu *superMenu = Supermenu();
236 	BMenuItem *selectedItem = FindItem(superItem->Label());
237 	BPoint point = superItem->Frame().LeftTop();
238 
239 	superMenu->ConvertToScreen(&point);
240 
241 	if (selectedItem != NULL)
242 		point.y -= selectedItem->Frame().top;
243 
244 	return point;
245 }
246 
247 
248 //	#pragma mark - private methods
249 
250 
251 void BPopUpMenu::_ReservedPopUpMenu1() {}
252 void BPopUpMenu::_ReservedPopUpMenu2() {}
253 void BPopUpMenu::_ReservedPopUpMenu3() {}
254 
255 
256 BPopUpMenu &
257 BPopUpMenu::operator=(const BPopUpMenu &)
258 {
259 	return *this;
260 }
261 
262 
263 BMenuItem *
264 BPopUpMenu::_go(BPoint where, bool autoInvoke, bool startOpened,
265 		BRect *_specialRect, bool async)
266 {
267 	BMenuItem *selected = NULL;
268 
269 	// Can't use Window(), as the BPopUpMenu isn't attached
270 	BLooper *looper = BLooper::LooperForThread(find_thread(NULL));
271 	BWindow *window = dynamic_cast<BWindow *>(looper);
272 
273 	popup_menu_data *data = new popup_menu_data;
274 	sem_id sem = create_sem(0, "window close lock");
275 
276 	data->window = window;
277 
278 	// Asynchronous menu: we set the BWindow menu's semaphore
279 	// and let BWindow block when needed
280 	if (async && window != NULL) {
281 		_set_menu_sem_(window, sem);
282 	}
283 
284 	data->object = this;
285 	data->autoInvoke = autoInvoke;
286 	data->useRect = _specialRect != NULL;
287 	if (_specialRect != NULL)
288 		data->rect = *_specialRect;
289 	data->async = async;
290 	data->where = where;
291 	data->startOpened = startOpened;
292 	data->selected = selected;
293 	data->lock = sem;
294 
295 	// Spawn the tracking thread
296 	fTrackThread = spawn_thread(entry, "popup", B_NORMAL_PRIORITY, data);
297 
298 	if (fTrackThread >= 0)
299 		resume_thread(fTrackThread);
300 	else {
301 		// Something went wrong. Cleanup and return NULL
302 		delete_sem(sem);
303 		if (async && window != NULL)
304 			_set_menu_sem_(window, B_BAD_SEM_ID);
305 		delete data;
306 		return NULL;
307 	}
308 
309 	// Synchronous menu: we block on the sem till
310 	// the other thread deletes it.
311 	if (!async) {
312 		if (window) {
313 			// TODO: usually it's not a good idea to check for a particular error
314 			// code. Though here we just want to wait till the semaphore is deleted
315 			// (it will return B_BAD_SEM_ID in that case), not provide locking or whatever.
316 			while (acquire_sem_etc(sem, 1, B_TIMEOUT, 50000) != B_BAD_SEM_ID) {
317 				window->UpdateIfNeeded();
318 			}
319 		}
320 
321 		status_t unused;
322 		while (wait_for_thread(fTrackThread, &unused) == B_INTERRUPTED)
323 			;
324 
325 		selected = data->selected;
326 		delete data;
327 	}
328 
329 	return selected;
330 }
331 
332 
333 int32
334 BPopUpMenu::entry(void *arg)
335 {
336 	popup_menu_data *data = static_cast<popup_menu_data *>(arg);
337 	BPopUpMenu *menu = data->object;
338 	BRect *rect = NULL;
339 
340 	if (data->useRect)
341 		rect = &data->rect;
342 
343 	data->selected = menu->start_track(data->where, data->autoInvoke, data->startOpened, rect);
344 
345 	// Reset the window menu semaphore
346 	if (data->async && data->window)
347 		_set_menu_sem_(data->window, B_BAD_SEM_ID);
348 
349 	delete_sem(data->lock);
350 
351 	// Commit suicide if needed
352 	if (menu->fAutoDestruct)
353 		delete menu;
354 
355 	if (data->async)
356 		delete data;
357 
358 	return 0;
359 }
360 
361 
362 BMenuItem *
363 BPopUpMenu::start_track(BPoint where, bool autoInvoke,
364 	bool startOpened, BRect *_specialRect)
365 {
366 	fWhere = where;
367 
368 	// I know, this doesn't look senseful, but don't be fooled,
369 	// fUseWhere is used in ScreenLocation(), which is a virtual
370 	// called by BMenu::Track()
371 	fUseWhere = true;
372 
373 	// Show the menu's window
374 	Show();
375 
376 	// Wait some time then track the menu
377 	snooze(50000);
378 	BMenuItem *result = Track(startOpened, _specialRect);
379 	if (result != NULL && autoInvoke)
380 		result->Invoke();
381 
382 	fUseWhere = false;
383 
384 	Hide();
385 	be_app->ShowCursor();
386 
387 	fTrackThread = -1;
388 
389 	return result;
390 }
391 
392