xref: /haiku/src/apps/icon-o-matic/generic/gui/stateview/StateView.cpp (revision 4f2fd49bdc6078128b1391191e4edac647044c3d)
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 // #pragma mark -
318 
319 // SetState
320 void
321 StateView::SetState(ViewState* state)
322 {
323 	if (fCurrentState == state)
324 		return;
325 
326 	// switch states as appropriate
327 	if (fCurrentState)
328 		fCurrentState->Cleanup();
329 
330 	fCurrentState = state;
331 
332 	if (fCurrentState)
333 		fCurrentState->Init();
334 }
335 
336 // UpdateStateCursor
337 void
338 StateView::UpdateStateCursor()
339 {
340 	if (!fCurrentState || !fCurrentState->UpdateCursor()) {
341 		SetViewCursor(B_CURSOR_SYSTEM_DEFAULT, true);
342 	}
343 }
344 
345 // Draw
346 void
347 StateView::Draw(BView* into, BRect updateRect)
348 {
349 	if (fLocker && !fLocker->ReadLock()) {
350 		return;
351 	}
352 
353 	if (fCurrentState)
354 		fCurrentState->Draw(into, updateRect);
355 
356 	if (fDropAnticipatingState)
357 		fDropAnticipatingState->Draw(into, updateRect);
358 
359 	if (fLocker)
360 		fLocker->ReadUnlock();
361 }
362 
363 // MouseWheelChanged
364 bool
365 StateView::MouseWheelChanged(BPoint where, float x, float y)
366 {
367 	return false;
368 }
369 
370 // HandleKeyDown
371 bool
372 StateView::HandleKeyDown(uint32 key, uint32 modifiers)
373 {
374 	// down't allow key events if mouse already pressed
375 	// (central place to prevent command stack mix up)
376 	if (fMouseInfo.buttons != 0)
377 		return false;
378 
379 	AutoWriteLocker locker(fLocker);
380 	if (fLocker && !locker.IsLocked())
381 		return false;
382 
383 	if (_HandleKeyDown(key, modifiers))
384 		return true;
385 
386 	if (fCurrentState) {
387 		Command* command = NULL;
388 		if (fCurrentState->HandleKeyDown(key, modifiers, &command)) {
389 			Perform(command);
390 			return true;
391 		}
392 	}
393 	return false;
394 }
395 
396 // HandleKeyUp
397 bool
398 StateView::HandleKeyUp(uint32 key, uint32 modifiers)
399 {
400 	// down't allow key events if mouse already pressed
401 	// (central place to prevent command stack mix up)
402 	if (fMouseInfo.buttons != 0)
403 		return false;
404 
405 	AutoWriteLocker locker(fLocker);
406 	if (fLocker && !locker.IsLocked())
407 		return false;
408 
409 	if (_HandleKeyUp(key, modifiers))
410 		return true;
411 
412 	if (fCurrentState) {
413 		Command* command = NULL;
414 		if (fCurrentState->HandleKeyUp(key, modifiers, &command)) {
415 			Perform(command);
416 			return true;
417 		}
418 	}
419 	return false;
420 }
421 
422 // FilterMouse
423 void
424 StateView::FilterMouse(BPoint* where) const
425 {
426 }
427 
428 // StateForDragMessage
429 ViewState*
430 StateView::StateForDragMessage(const BMessage* message)
431 {
432 	return NULL;
433 }
434 
435 // SetCommandStack
436 void
437 StateView::SetCommandStack(::CommandStack* stack)
438 {
439 	fCommandStack = stack;
440 }
441 
442 // SetLocker
443 void
444 StateView::SetLocker(RWLocker* locker)
445 {
446 	fLocker = locker;
447 }
448 
449 // SetUpdateTarget
450 void
451 StateView::SetUpdateTarget(BHandler* target, uint32 command)
452 {
453 	fUpdateTarget = target;
454 	fUpdateCommand = command;
455 }
456 
457 // SetCatchAllEvents
458 void
459 StateView::SetCatchAllEvents(bool catchAll)
460 {
461 	if (fCatchAllEvents == catchAll)
462 		return;
463 
464 	fCatchAllEvents = catchAll;
465 
466 	if (fCatchAllEvents)
467 		_InstallEventFilter();
468 	else
469 		_RemoveEventFilter();
470 }
471 
472 // Perform
473 status_t
474 StateView::Perform(Command* command)
475 {
476 	if (fCommandStack)
477 		return fCommandStack->Perform(command);
478 
479 	// if there is no command stack, then nobody
480 	// else feels responsible...
481 	delete command;
482 
483 	return B_NO_INIT;
484 }
485 
486 // #pragma mark -
487 
488 // _HandleKeyDown
489 bool
490 StateView::_HandleKeyDown(uint32 key, uint32 modifiers)
491 {
492 	return false;
493 }
494 
495 // _HandleKeyUp
496 bool
497 StateView::_HandleKeyUp(uint32 key, uint32 modifiers)
498 {
499 	return false;
500 }
501 
502 // _InstallEventFilter
503 void
504 StateView::_InstallEventFilter()
505 {
506 	if (!fCatchAllEvents)
507 		return;
508 
509 	if (!fEventFilter)
510 		fEventFilter = new (nothrow) EventFilter(this);
511 
512 	if (!fEventFilter || !Window())
513 		return;
514 
515 	Window()->AddCommonFilter(fEventFilter);
516 }
517 
518 void
519 StateView::_RemoveEventFilter()
520 {
521 	if (!fEventFilter || !Window())
522 		return;
523 
524 	Window()->RemoveCommonFilter(fEventFilter);
525 }
526 
527 // _TriggerUpdate
528 void
529 StateView::_TriggerUpdate()
530 {
531 	if (fUpdateTarget && fUpdateTarget->Looper()) {
532 		fUpdateTarget->Looper()->PostMessage(fUpdateCommand);
533 	}
534 }
535