xref: /haiku/src/apps/icon-o-matic/generic/gui/stateview/StateView.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
1 /*
2  * Copyright 2006-2007, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stephan Aßmus <superstippi@gmx.de>
7  */
8 
9 #include "StateView.h"
10 
11 #include <new>
12 
13 #include <Message.h>
14 #include <MessageFilter.h>
15 #include <TextView.h>
16 #include <Window.h>
17 
18 #include "Command.h"
19 #include "CommandStack.h"
20 // TODO: hack - somehow figure out of catching
21 // key events for a given control is ok
22 #include "GradientControl.h"
23 #include "ListViews.h"
24 //
25 #include "RWLocker.h"
26 
27 using std::nothrow;
28 
29 class EventFilter : public BMessageFilter {
30  public:
31 	EventFilter(StateView* target)
32 		: BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
33 		  fTarget(target)
34 		{
35 		}
36 	virtual	~EventFilter()
37 		{
38 		}
39 	virtual	filter_result	Filter(BMessage* message, BHandler** target)
40 		{
41 			filter_result result = B_DISPATCH_MESSAGE;
42 			switch (message->what) {
43 				case B_KEY_DOWN: {
44 if (dynamic_cast<BTextView*>(*target))
45 	break;
46 if (dynamic_cast<SimpleListView*>(*target))
47 	break;
48 if (dynamic_cast<GradientControl*>(*target))
49 	break;
50 					uint32 key;
51 					uint32 modifiers;
52 					if (message->FindInt32("raw_char", (int32*)&key) >= B_OK
53 						&& message->FindInt32("modifiers", (int32*)&modifiers) >= B_OK)
54 						if (fTarget->HandleKeyDown(key, modifiers))
55 							result = B_SKIP_MESSAGE;
56 					break;
57 				}
58 				case B_KEY_UP: {
59 if (dynamic_cast<BTextView*>(*target))
60 	break;
61 if (dynamic_cast<SimpleListView*>(*target))
62 	break;
63 if (dynamic_cast<GradientControl*>(*target))
64 	break;
65 					uint32 key;
66 					uint32 modifiers;
67 					if (message->FindInt32("raw_char", (int32*)&key) >= B_OK
68 						&& message->FindInt32("modifiers", (int32*)&modifiers) >= B_OK)
69 						if (fTarget->HandleKeyUp(key, modifiers))
70 							result = B_SKIP_MESSAGE;
71 					break;
72 
73 				}
74 				case B_MODIFIERS_CHANGED:
75 					*target = fTarget;
76 					break;
77 
78 				case B_MOUSE_WHEEL_CHANGED: {
79 					float x;
80 					float y;
81 					if (message->FindFloat("be:wheel_delta_x", &x) >= B_OK
82 						&& message->FindFloat("be:wheel_delta_y", &y) >= B_OK) {
83 						if (fTarget->MouseWheelChanged(
84 								fTarget->MouseInfo()->position, x, y))
85 							result = B_SKIP_MESSAGE;
86 					}
87 					break;
88 				}
89 				default:
90 					break;
91 			}
92 			return result;
93 		}
94  private:
95  	StateView*		fTarget;
96 };
97 
98 // #pragma mark -
99 
100 // constructor
101 StateView::StateView(BRect frame, const char* name,
102 					 uint32 resizingMode, uint32 flags)
103 	: BView(frame, name, resizingMode, flags),
104 	  fCurrentState(NULL),
105 	  fDropAnticipatingState(NULL),
106 
107 	  fMouseInfo(),
108 
109 	  fCommandStack(NULL),
110 	  fLocker(NULL),
111 
112 	  fEventFilter(NULL),
113 	  fCatchAllEvents(false),
114 
115 	  fUpdateTarget(NULL),
116 	  fUpdateCommand(0)
117 {
118 }
119 
120 // destructor
121 StateView::~StateView()
122 {
123 	delete fEventFilter;
124 }
125 
126 // #pragma mark -
127 
128 // AttachedToWindow
129 void
130 StateView::AttachedToWindow()
131 {
132 	_InstallEventFilter();
133 
134 	BView::AttachedToWindow();
135 }
136 
137 // DetachedFromWindow
138 void
139 StateView::DetachedFromWindow()
140 {
141 	_RemoveEventFilter();
142 
143 	BView::DetachedFromWindow();
144 }
145 
146 // Draw
147 void
148 StateView::Draw(BRect updateRect)
149 {
150 	Draw(this, updateRect);
151 }
152 
153 // MessageReceived
154 void
155 StateView::MessageReceived(BMessage* message)
156 {
157 	// let the state handle the message if it wants
158 	if (fCurrentState) {
159 		AutoWriteLocker locker(fLocker);
160 		if (fLocker && !locker.IsLocked())
161 			return;
162 
163 		Command* command = NULL;
164 		if (fCurrentState->MessageReceived(message, &command)) {
165 			Perform(command);
166 			return;
167 		}
168 	}
169 
170 	switch (message->what) {
171 		case B_MODIFIERS_CHANGED:
172 			// NOTE: received only if the view has focus!!
173 			if (fCurrentState) {
174 				uint32 mods;
175 				if (message->FindInt32("modifiers", (int32*)&mods) != B_OK)
176 					mods = modifiers();
177 				fCurrentState->ModifiersChanged(mods);
178 				fMouseInfo.modifiers = mods;
179 			}
180 			break;
181 		default:
182 			BView::MessageReceived(message);
183 	}
184 }
185 
186 // #pragma mark -
187 
188 // MouseDown
189 void
190 StateView::MouseDown(BPoint where)
191 {
192 	if (fLocker && !fLocker->WriteLock())
193 		return;
194 
195 	// query more info from the windows current message if available
196 	uint32 buttons;
197 	uint32 clicks;
198 	BMessage* message = Window() ? Window()->CurrentMessage() : NULL;
199 	if (!message || message->FindInt32("buttons", (int32*)&buttons) != B_OK)
200 		buttons = B_PRIMARY_MOUSE_BUTTON;
201 	if (!message || message->FindInt32("clicks", (int32*)&clicks) != B_OK)
202 		clicks = 1;
203 
204 	if (fCurrentState)
205 		fCurrentState->MouseDown(where, buttons, clicks);
206 
207 	// update mouse info *after* having called the ViewState hook
208 	fMouseInfo.buttons = buttons;
209 	fMouseInfo.position = where;
210 
211 	if (fLocker)
212 		fLocker->WriteUnlock();
213 }
214 
215 // MouseMoved
216 void
217 StateView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
218 {
219 	if (fLocker && !fLocker->WriteLock())
220 		return;
221 
222 	if (dragMessage && !fDropAnticipatingState) {
223 		// switch to a drop anticipating state if there is one available
224 		fDropAnticipatingState = StateForDragMessage(dragMessage);
225 		if (fDropAnticipatingState)
226 			fDropAnticipatingState->Init();
227 	}
228 
229 	// TODO: I don't like this too much
230 	if (!dragMessage && fDropAnticipatingState) {
231 		fDropAnticipatingState->Cleanup();
232 		fDropAnticipatingState = NULL;
233 	}
234 
235 	if (fDropAnticipatingState)
236 		fDropAnticipatingState->MouseMoved(where, transit, dragMessage);
237 	else {
238 		if (fCurrentState) {
239 			fCurrentState->MouseMoved(where, transit, dragMessage);
240 			if (fMouseInfo.buttons != 0)
241 				_TriggerUpdate();
242 		}
243 	}
244 
245 	// update mouse info *after* having called the ViewState hook
246 	fMouseInfo.position = where;
247 	fMouseInfo.transit = transit;
248 
249 	if (fLocker)
250 		fLocker->WriteUnlock();
251 }
252 
253 // MouseUp
254 void
255 StateView::MouseUp(BPoint where)
256 {
257 	if (fLocker && !fLocker->WriteLock())
258 		return;
259 
260 	if (fDropAnticipatingState) {
261 		Perform(fDropAnticipatingState->MouseUp());
262 		fDropAnticipatingState->Cleanup();
263 		fDropAnticipatingState = NULL;
264 
265 		if (fCurrentState) {
266 			fCurrentState->MouseMoved(fMouseInfo.position, fMouseInfo.transit,
267 									  NULL);
268 		}
269 	} else {
270 		if (fCurrentState) {
271 			Perform(fCurrentState->MouseUp());
272 			_TriggerUpdate();
273 		}
274 	}
275 
276 	// update mouse info *after* having called the ViewState hook
277 	fMouseInfo.buttons = 0;
278 
279 	if (fLocker)
280 		fLocker->WriteUnlock();
281 }
282 
283 // #pragma mark -
284 
285 // KeyDown
286 void
287 StateView::KeyDown(const char* bytes, int32 numBytes)
288 {
289 	uint32 key;
290 	uint32 modifiers;
291 	BMessage* message = Window() ? Window()->CurrentMessage() : NULL;
292 	if (message
293 		&& message->FindInt32("raw_char", (int32*)&key) >= B_OK
294 		&& message->FindInt32("modifiers", (int32*)&modifiers) >= B_OK) {
295 		if (HandleKeyDown(key, modifiers))
296 			return;
297 	}
298 	BView::KeyDown(bytes, numBytes);
299 }
300 
301 // KeyUp
302 void
303 StateView::KeyUp(const char* bytes, int32 numBytes)
304 {
305 	uint32 key;
306 	uint32 modifiers;
307 	BMessage* message = Window() ? Window()->CurrentMessage() : NULL;
308 	if (message
309 		&& message->FindInt32("raw_char", (int32*)&key) >= B_OK
310 		&& message->FindInt32("modifiers", (int32*)&modifiers) >= B_OK) {
311 		if (HandleKeyUp(key, modifiers))
312 			return;
313 	}
314 	BView::KeyUp(bytes, numBytes);
315 }
316 
317 
318 // #pragma mark -
319 
320 
321 status_t
322 StateView::Perform(perform_code code, void* data)
323 {
324 	return BView::Perform(code, data);
325 }
326 
327 
328 // #pragma mark -
329 
330 // SetState
331 void
332 StateView::SetState(ViewState* state)
333 {
334 	if (fCurrentState == state)
335 		return;
336 
337 	// switch states as appropriate
338 	if (fCurrentState)
339 		fCurrentState->Cleanup();
340 
341 	fCurrentState = state;
342 
343 	if (fCurrentState)
344 		fCurrentState->Init();
345 }
346 
347 // UpdateStateCursor
348 void
349 StateView::UpdateStateCursor()
350 {
351 	if (!fCurrentState || !fCurrentState->UpdateCursor()) {
352 		SetViewCursor(B_CURSOR_SYSTEM_DEFAULT, true);
353 	}
354 }
355 
356 // Draw
357 void
358 StateView::Draw(BView* into, BRect updateRect)
359 {
360 	if (fLocker && !fLocker->ReadLock()) {
361 		return;
362 	}
363 
364 	if (fCurrentState)
365 		fCurrentState->Draw(into, updateRect);
366 
367 	if (fDropAnticipatingState)
368 		fDropAnticipatingState->Draw(into, updateRect);
369 
370 	if (fLocker)
371 		fLocker->ReadUnlock();
372 }
373 
374 // MouseWheelChanged
375 bool
376 StateView::MouseWheelChanged(BPoint where, float x, float y)
377 {
378 	return false;
379 }
380 
381 // HandleKeyDown
382 bool
383 StateView::HandleKeyDown(uint32 key, uint32 modifiers)
384 {
385 	// down't allow key events if mouse already pressed
386 	// (central place to prevent command stack mix up)
387 	if (fMouseInfo.buttons != 0)
388 		return false;
389 
390 	AutoWriteLocker locker(fLocker);
391 	if (fLocker && !locker.IsLocked())
392 		return false;
393 
394 	if (_HandleKeyDown(key, modifiers))
395 		return true;
396 
397 	if (fCurrentState) {
398 		Command* command = NULL;
399 		if (fCurrentState->HandleKeyDown(key, modifiers, &command)) {
400 			Perform(command);
401 			return true;
402 		}
403 	}
404 	return false;
405 }
406 
407 // HandleKeyUp
408 bool
409 StateView::HandleKeyUp(uint32 key, uint32 modifiers)
410 {
411 	// down't allow key events if mouse already pressed
412 	// (central place to prevent command stack mix up)
413 	if (fMouseInfo.buttons != 0)
414 		return false;
415 
416 	AutoWriteLocker locker(fLocker);
417 	if (fLocker && !locker.IsLocked())
418 		return false;
419 
420 	if (_HandleKeyUp(key, modifiers))
421 		return true;
422 
423 	if (fCurrentState) {
424 		Command* command = NULL;
425 		if (fCurrentState->HandleKeyUp(key, modifiers, &command)) {
426 			Perform(command);
427 			return true;
428 		}
429 	}
430 	return false;
431 }
432 
433 // FilterMouse
434 void
435 StateView::FilterMouse(BPoint* where) const
436 {
437 }
438 
439 // StateForDragMessage
440 ViewState*
441 StateView::StateForDragMessage(const BMessage* message)
442 {
443 	return NULL;
444 }
445 
446 // SetCommandStack
447 void
448 StateView::SetCommandStack(::CommandStack* stack)
449 {
450 	fCommandStack = stack;
451 }
452 
453 // SetLocker
454 void
455 StateView::SetLocker(RWLocker* locker)
456 {
457 	fLocker = locker;
458 }
459 
460 // SetUpdateTarget
461 void
462 StateView::SetUpdateTarget(BHandler* target, uint32 command)
463 {
464 	fUpdateTarget = target;
465 	fUpdateCommand = command;
466 }
467 
468 // SetCatchAllEvents
469 void
470 StateView::SetCatchAllEvents(bool catchAll)
471 {
472 	if (fCatchAllEvents == catchAll)
473 		return;
474 
475 	fCatchAllEvents = catchAll;
476 
477 	if (fCatchAllEvents)
478 		_InstallEventFilter();
479 	else
480 		_RemoveEventFilter();
481 }
482 
483 // Perform
484 status_t
485 StateView::Perform(Command* command)
486 {
487 	if (fCommandStack)
488 		return fCommandStack->Perform(command);
489 
490 	// if there is no command stack, then nobody
491 	// else feels responsible...
492 	delete command;
493 
494 	return B_NO_INIT;
495 }
496 
497 // #pragma mark -
498 
499 // _HandleKeyDown
500 bool
501 StateView::_HandleKeyDown(uint32 key, uint32 modifiers)
502 {
503 	return false;
504 }
505 
506 // _HandleKeyUp
507 bool
508 StateView::_HandleKeyUp(uint32 key, uint32 modifiers)
509 {
510 	return false;
511 }
512 
513 // _InstallEventFilter
514 void
515 StateView::_InstallEventFilter()
516 {
517 	if (!fCatchAllEvents)
518 		return;
519 
520 	if (!fEventFilter)
521 		fEventFilter = new (nothrow) EventFilter(this);
522 
523 	if (!fEventFilter || !Window())
524 		return;
525 
526 	Window()->AddCommonFilter(fEventFilter);
527 }
528 
529 void
530 StateView::_RemoveEventFilter()
531 {
532 	if (!fEventFilter || !Window())
533 		return;
534 
535 	Window()->RemoveCommonFilter(fEventFilter);
536 }
537 
538 // _TriggerUpdate
539 void
540 StateView::_TriggerUpdate()
541 {
542 	if (fUpdateTarget && fUpdateTarget->Looper()) {
543 		fUpdateTarget->Looper()->PostMessage(fUpdateCommand);
544 	}
545 }
546