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:
EventFilter(StateView * target)32 EventFilter(StateView* target)
33 : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
34 fTarget(target)
35 {
36 }
~EventFilter()37 virtual ~EventFilter()
38 {
39 }
Filter(BMessage * message,BHandler ** target)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
StateView(BRect frame,const char * name,uint32 resizingMode,uint32 flags)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
~StateView()125 StateView::~StateView()
126 {
127 delete fEventFilter;
128 }
129
130
131 // #pragma mark -
132
133
134 void
AttachedToWindow()135 StateView::AttachedToWindow()
136 {
137 _InstallEventFilter();
138
139 BView::AttachedToWindow();
140 }
141
142
143 void
DetachedFromWindow()144 StateView::DetachedFromWindow()
145 {
146 _RemoveEventFilter();
147
148 BView::DetachedFromWindow();
149 }
150
151
152 void
Draw(BRect updateRect)153 StateView::Draw(BRect updateRect)
154 {
155 Draw(this, updateRect);
156 }
157
158
159 void
MessageReceived(BMessage * message)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
MouseDown(BPoint where)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
MouseMoved(BPoint where,uint32 transit,const BMessage * dragMessage)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
MouseUp(BPoint where)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
KeyDown(const char * bytes,int32 numBytes)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
KeyUp(const char * bytes,int32 numBytes)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
Perform(perform_code code,void * data)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
GetPreferredSize(float * width,float * height)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
SetState(ViewState * state)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
UpdateStateCursor()370 StateView::UpdateStateCursor()
371 {
372 if (!fCurrentState || !fCurrentState->UpdateCursor()) {
373 SetViewCursor(B_CURSOR_SYSTEM_DEFAULT, true);
374 }
375 }
376
377
378 void
Draw(BView * into,BRect updateRect)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
MouseWheelChanged(BPoint where,float x,float y)397 StateView::MouseWheelChanged(BPoint where, float x, float y)
398 {
399 return false;
400 }
401
402
403 bool
HandleKeyDown(uint32 key,uint32 modifiers)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
HandleKeyUp(uint32 key,uint32 modifiers)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
FilterMouse(BPoint * where) const456 StateView::FilterMouse(BPoint* where) const
457 {
458 }
459
460
461 ViewState*
StateForDragMessage(const BMessage * message)462 StateView::StateForDragMessage(const BMessage* message)
463 {
464 return NULL;
465 }
466
467
468 void
SetCommandStack(::CommandStack * stack)469 StateView::SetCommandStack(::CommandStack* stack)
470 {
471 fCommandStack = stack;
472 }
473
474
475 void
SetLocker(RWLocker * locker)476 StateView::SetLocker(RWLocker* locker)
477 {
478 fLocker = locker;
479 }
480
481
482 void
SetUpdateTarget(BHandler * target,uint32 command)483 StateView::SetUpdateTarget(BHandler* target, uint32 command)
484 {
485 fUpdateTarget = target;
486 fUpdateCommand = command;
487 }
488
489
490 void
SetCatchAllEvents(bool catchAll)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
Perform(Command * command)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
_HandleKeyDown(uint32 key,uint32 modifiers)523 StateView::_HandleKeyDown(uint32 key, uint32 modifiers)
524 {
525 return false;
526 }
527
528
529 bool
_HandleKeyUp(uint32 key,uint32 modifiers)530 StateView::_HandleKeyUp(uint32 key, uint32 modifiers)
531 {
532 return false;
533 }
534
535
536 void
_InstallEventFilter()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
_RemoveEventFilter()553 StateView::_RemoveEventFilter()
554 {
555 if (!fEventFilter || !Window())
556 return;
557
558 Window()->RemoveCommonFilter(fEventFilter);
559 }
560
561 // _TriggerUpdate
562 void
_TriggerUpdate()563 StateView::_TriggerUpdate()
564 {
565 if (fUpdateTarget && fUpdateTarget->Looper()) {
566 fUpdateTarget->Looper()->PostMessage(fUpdateCommand);
567 }
568 }
569