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