xref: /haiku/src/apps/icon-o-matic/generic/gui/stateview/StateView.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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 
28 using std::nothrow;
29 
30 class 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: {
45 if (dynamic_cast<BTextView*>(*target))
46 	break;
47 if (dynamic_cast<SimpleListView*>(*target))
48 	break;
49 if (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: {
60 if (dynamic_cast<BTextView*>(*target))
61 	break;
62 if (dynamic_cast<SimpleListView*>(*target))
63 	break;
64 if (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 
103 StateView::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 
125 StateView::~StateView()
126 {
127 	delete fEventFilter;
128 }
129 
130 
131 // #pragma mark -
132 
133 
134 void
135 StateView::AttachedToWindow()
136 {
137 	_InstallEventFilter();
138 
139 	BView::AttachedToWindow();
140 }
141 
142 
143 void
144 StateView::DetachedFromWindow()
145 {
146 	_RemoveEventFilter();
147 
148 	BView::DetachedFromWindow();
149 }
150 
151 
152 void
153 StateView::Draw(BRect updateRect)
154 {
155 	Draw(this, updateRect);
156 }
157 
158 
159 void
160 StateView::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 
195 void
196 StateView::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 
222 void
223 StateView::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 
260 void
261 StateView::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 
293 void
294 StateView::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 
309 void
310 StateView::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 
328 status_t
329 StateView::Perform(perform_code code, void* data)
330 {
331 	return BView::Perform(code, data);
332 }
333 
334 
335 // #pragma mark -
336 
337 
338 void
339 StateView::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 
352 void
353 StateView::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 
369 void
370 StateView::UpdateStateCursor()
371 {
372 	if (!fCurrentState || !fCurrentState->UpdateCursor()) {
373 		SetViewCursor(B_CURSOR_SYSTEM_DEFAULT, true);
374 	}
375 }
376 
377 
378 void
379 StateView::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 
396 bool
397 StateView::MouseWheelChanged(BPoint where, float x, float y)
398 {
399 	return false;
400 }
401 
402 
403 bool
404 StateView::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 
429 bool
430 StateView::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 
455 void
456 StateView::FilterMouse(BPoint* where) const
457 {
458 }
459 
460 
461 ViewState*
462 StateView::StateForDragMessage(const BMessage* message)
463 {
464 	return NULL;
465 }
466 
467 
468 void
469 StateView::SetCommandStack(::CommandStack* stack)
470 {
471 	fCommandStack = stack;
472 }
473 
474 
475 void
476 StateView::SetLocker(RWLocker* locker)
477 {
478 	fLocker = locker;
479 }
480 
481 
482 void
483 StateView::SetUpdateTarget(BHandler* target, uint32 command)
484 {
485 	fUpdateTarget = target;
486 	fUpdateCommand = command;
487 }
488 
489 
490 void
491 StateView::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 
505 status_t
506 StateView::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 
522 bool
523 StateView::_HandleKeyDown(uint32 key, uint32 modifiers)
524 {
525 	return false;
526 }
527 
528 
529 bool
530 StateView::_HandleKeyUp(uint32 key, uint32 modifiers)
531 {
532 	return false;
533 }
534 
535 
536 void
537 StateView::_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 
552 void
553 StateView::_RemoveEventFilter()
554 {
555 	if (!fEventFilter || !Window())
556 		return;
557 
558 	Window()->RemoveCommonFilter(fEventFilter);
559 }
560 
561 // _TriggerUpdate
562 void
563 StateView::_TriggerUpdate()
564 {
565 	if (fUpdateTarget && fUpdateTarget->Looper()) {
566 		fUpdateTarget->Looper()->PostMessage(fUpdateCommand);
567 	}
568 }
569