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