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
27using std::nothrow;
28
29class 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: {
44if (dynamic_cast<BTextView*>(*target))
45	break;
46if (dynamic_cast<SimpleListView*>(*target))
47	break;
48if (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: {
59if (dynamic_cast<BTextView*>(*target))
60	break;
61if (dynamic_cast<SimpleListView*>(*target))
62	break;
63if (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
101StateView::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
121StateView::~StateView()
122{
123	delete fEventFilter;
124}
125
126// #pragma mark -
127
128// AttachedToWindow
129void
130StateView::AttachedToWindow()
131{
132	_InstallEventFilter();
133
134	BView::AttachedToWindow();
135}
136
137// DetachedFromWindow
138void
139StateView::DetachedFromWindow()
140{
141	_RemoveEventFilter();
142
143	BView::DetachedFromWindow();
144}
145
146// Draw
147void
148StateView::Draw(BRect updateRect)
149{
150	Draw(this, updateRect);
151}
152
153// MessageReceived
154void
155StateView::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
189void
190StateView::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
216void
217StateView::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
254void
255StateView::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
286void
287StateView::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
302void
303StateView::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
321status_t
322StateView::Perform(perform_code code, void* data)
323{
324	return BView::Perform(code, data);
325}
326
327
328// #pragma mark -
329
330// SetState
331void
332StateView::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
348void
349StateView::UpdateStateCursor()
350{
351	if (!fCurrentState || !fCurrentState->UpdateCursor()) {
352		SetViewCursor(B_CURSOR_SYSTEM_DEFAULT, true);
353	}
354}
355
356// Draw
357void
358StateView::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
375bool
376StateView::MouseWheelChanged(BPoint where, float x, float y)
377{
378	return false;
379}
380
381// HandleKeyDown
382bool
383StateView::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
408bool
409StateView::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
434void
435StateView::FilterMouse(BPoint* where) const
436{
437}
438
439// StateForDragMessage
440ViewState*
441StateView::StateForDragMessage(const BMessage* message)
442{
443	return NULL;
444}
445
446// SetCommandStack
447void
448StateView::SetCommandStack(::CommandStack* stack)
449{
450	fCommandStack = stack;
451}
452
453// SetLocker
454void
455StateView::SetLocker(RWLocker* locker)
456{
457	fLocker = locker;
458}
459
460// SetUpdateTarget
461void
462StateView::SetUpdateTarget(BHandler* target, uint32 command)
463{
464	fUpdateTarget = target;
465	fUpdateCommand = command;
466}
467
468// SetCatchAllEvents
469void
470StateView::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
484status_t
485StateView::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
500bool
501StateView::_HandleKeyDown(uint32 key, uint32 modifiers)
502{
503	return false;
504}
505
506// _HandleKeyUp
507bool
508StateView::_HandleKeyUp(uint32 key, uint32 modifiers)
509{
510	return false;
511}
512
513// _InstallEventFilter
514void
515StateView::_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
529void
530StateView::_RemoveEventFilter()
531{
532	if (!fEventFilter || !Window())
533		return;
534
535	Window()->RemoveCommonFilter(fEventFilter);
536}
537
538// _TriggerUpdate
539void
540StateView::_TriggerUpdate()
541{
542	if (fUpdateTarget && fUpdateTarget->Looper()) {
543		fUpdateTarget->Looper()->PostMessage(fUpdateCommand);
544	}
545}
546