/* * Copyright 2006-2007, 2023, Haiku. * Distributed under the terms of the MIT License. * * Authors: * Stephan Aßmus * Zardshard */ #include "StateView.h" #include #include #include #include #include #include "Command.h" #include "CommandStack.h" // TODO: hack - somehow figure out of catching // key events for a given control is ok #include "GradientControl.h" #include "ListViews.h" // #include "RWLocker.h" using std::nothrow; class EventFilter : public BMessageFilter { public: EventFilter(StateView* target) : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE), fTarget(target) { } virtual ~EventFilter() { } virtual filter_result Filter(BMessage* message, BHandler** target) { filter_result result = B_DISPATCH_MESSAGE; switch (message->what) { case B_KEY_DOWN: { if (dynamic_cast(*target)) break; if (dynamic_cast(*target)) break; if (dynamic_cast(*target)) break; uint32 key; uint32 modifiers; if (message->FindInt32("raw_char", (int32*)&key) >= B_OK && message->FindInt32("modifiers", (int32*)&modifiers) >= B_OK) if (fTarget->HandleKeyDown(key, modifiers)) result = B_SKIP_MESSAGE; break; } case B_KEY_UP: { if (dynamic_cast(*target)) break; if (dynamic_cast(*target)) break; if (dynamic_cast(*target)) break; uint32 key; uint32 modifiers; if (message->FindInt32("raw_char", (int32*)&key) >= B_OK && message->FindInt32("modifiers", (int32*)&modifiers) >= B_OK) if (fTarget->HandleKeyUp(key, modifiers)) result = B_SKIP_MESSAGE; break; } case B_MODIFIERS_CHANGED: *target = fTarget; break; case B_MOUSE_WHEEL_CHANGED: { float x; float y; if (message->FindFloat("be:wheel_delta_x", &x) >= B_OK && message->FindFloat("be:wheel_delta_y", &y) >= B_OK) { if (fTarget->MouseWheelChanged( fTarget->MouseInfo()->position, x, y)) result = B_SKIP_MESSAGE; } break; } default: break; } return result; } private: StateView* fTarget; }; // #pragma mark - StateView::StateView(BRect frame, const char* name, uint32 resizingMode, uint32 flags) : BView(frame, name, resizingMode, flags), fStartingRect(frame), fCurrentState(NULL), fDropAnticipatingState(NULL), fMouseInfo(), fCommandStack(NULL), fLocker(NULL), fEventFilter(NULL), fCatchAllEvents(false), fUpdateTarget(NULL), fUpdateCommand(0) { } StateView::~StateView() { delete fEventFilter; } // #pragma mark - void StateView::AttachedToWindow() { _InstallEventFilter(); BView::AttachedToWindow(); } void StateView::DetachedFromWindow() { _RemoveEventFilter(); BView::DetachedFromWindow(); } void StateView::Draw(BRect updateRect) { Draw(this, updateRect); } void StateView::MessageReceived(BMessage* message) { // let the state handle the message if it wants if (fCurrentState) { AutoWriteLocker locker(fLocker); if (fLocker && !locker.IsLocked()) return; Command* command = NULL; if (fCurrentState->MessageReceived(message, &command)) { Perform(command); return; } } switch (message->what) { case B_MODIFIERS_CHANGED: // NOTE: received only if the view has focus!! if (fCurrentState) { uint32 mods; if (message->FindInt32("modifiers", (int32*)&mods) != B_OK) mods = modifiers(); fCurrentState->ModifiersChanged(mods); fMouseInfo.modifiers = mods; } break; default: BView::MessageReceived(message); } } // #pragma mark - void StateView::MouseDown(BPoint where) { if (fLocker && !fLocker->WriteLock()) return; // query more info from the windows current message if available uint32 buttons; uint32 clicks; BMessage* message = Window() ? Window()->CurrentMessage() : NULL; if (!message || message->FindInt32("buttons", (int32*)&buttons) != B_OK) buttons = B_PRIMARY_MOUSE_BUTTON; if (!message || message->FindInt32("clicks", (int32*)&clicks) != B_OK) clicks = 1; if (fCurrentState) fCurrentState->MouseDown(where, buttons, clicks); // update mouse info *after* having called the ViewState hook fMouseInfo.buttons = buttons; fMouseInfo.position = where; if (fLocker) fLocker->WriteUnlock(); } void StateView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage) { if (fLocker && !fLocker->WriteLock()) return; if (dragMessage && !fDropAnticipatingState) { // switch to a drop anticipating state if there is one available fDropAnticipatingState = StateForDragMessage(dragMessage); if (fDropAnticipatingState) fDropAnticipatingState->Init(); } // TODO: I don't like this too much if (!dragMessage && fDropAnticipatingState) { fDropAnticipatingState->Cleanup(); fDropAnticipatingState = NULL; } if (fDropAnticipatingState) fDropAnticipatingState->MouseMoved(where, transit, dragMessage); else { if (fCurrentState) { fCurrentState->MouseMoved(where, transit, dragMessage); if (fMouseInfo.buttons != 0) _TriggerUpdate(); } } // update mouse info *after* having called the ViewState hook fMouseInfo.position = where; fMouseInfo.transit = transit; if (fLocker) fLocker->WriteUnlock(); } void StateView::MouseUp(BPoint where) { if (fLocker && !fLocker->WriteLock()) return; if (fDropAnticipatingState) { Perform(fDropAnticipatingState->MouseUp()); fDropAnticipatingState->Cleanup(); fDropAnticipatingState = NULL; if (fCurrentState) { fCurrentState->MouseMoved(fMouseInfo.position, fMouseInfo.transit, NULL); } } else { if (fCurrentState) { Perform(fCurrentState->MouseUp()); _TriggerUpdate(); } } // update mouse info *after* having called the ViewState hook fMouseInfo.buttons = 0; if (fLocker) fLocker->WriteUnlock(); } // #pragma mark - void StateView::KeyDown(const char* bytes, int32 numBytes) { uint32 key; uint32 modifiers; BMessage* message = Window() ? Window()->CurrentMessage() : NULL; if (message && message->FindInt32("raw_char", (int32*)&key) >= B_OK && message->FindInt32("modifiers", (int32*)&modifiers) >= B_OK) { if (HandleKeyDown(key, modifiers)) return; } BView::KeyDown(bytes, numBytes); } void StateView::KeyUp(const char* bytes, int32 numBytes) { uint32 key; uint32 modifiers; BMessage* message = Window() ? Window()->CurrentMessage() : NULL; if (message && message->FindInt32("raw_char", (int32*)&key) >= B_OK && message->FindInt32("modifiers", (int32*)&modifiers) >= B_OK) { if (HandleKeyUp(key, modifiers)) return; } BView::KeyUp(bytes, numBytes); } // #pragma mark - status_t StateView::Perform(perform_code code, void* data) { return BView::Perform(code, data); } // #pragma mark - void StateView::GetPreferredSize(float* width, float* height) { if (width != NULL) *width = fStartingRect.Width(); if (height != NULL) *height = fStartingRect.Height(); } // #pragma mark - void StateView::SetState(ViewState* state) { if (fCurrentState == state) return; // switch states as appropriate if (fCurrentState) fCurrentState->Cleanup(); fCurrentState = state; if (fCurrentState) fCurrentState->Init(); } void StateView::UpdateStateCursor() { if (!fCurrentState || !fCurrentState->UpdateCursor()) { SetViewCursor(B_CURSOR_SYSTEM_DEFAULT, true); } } void StateView::Draw(BView* into, BRect updateRect) { if (fLocker && !fLocker->ReadLock()) { return; } if (fCurrentState) fCurrentState->Draw(into, updateRect); if (fDropAnticipatingState) fDropAnticipatingState->Draw(into, updateRect); if (fLocker) fLocker->ReadUnlock(); } bool StateView::MouseWheelChanged(BPoint where, float x, float y) { return false; } bool StateView::HandleKeyDown(uint32 key, uint32 modifiers) { // down't allow key events if mouse already pressed // (central place to prevent command stack mix up) if (fMouseInfo.buttons != 0) return false; AutoWriteLocker locker(fLocker); if (fLocker && !locker.IsLocked()) return false; if (_HandleKeyDown(key, modifiers)) return true; if (fCurrentState) { Command* command = NULL; if (fCurrentState->HandleKeyDown(key, modifiers, &command)) { Perform(command); return true; } } return false; } bool StateView::HandleKeyUp(uint32 key, uint32 modifiers) { // down't allow key events if mouse already pressed // (central place to prevent command stack mix up) if (fMouseInfo.buttons != 0) return false; AutoWriteLocker locker(fLocker); if (fLocker && !locker.IsLocked()) return false; if (_HandleKeyUp(key, modifiers)) return true; if (fCurrentState) { Command* command = NULL; if (fCurrentState->HandleKeyUp(key, modifiers, &command)) { Perform(command); return true; } } return false; } void StateView::FilterMouse(BPoint* where) const { } ViewState* StateView::StateForDragMessage(const BMessage* message) { return NULL; } void StateView::SetCommandStack(::CommandStack* stack) { fCommandStack = stack; } void StateView::SetLocker(RWLocker* locker) { fLocker = locker; } void StateView::SetUpdateTarget(BHandler* target, uint32 command) { fUpdateTarget = target; fUpdateCommand = command; } void StateView::SetCatchAllEvents(bool catchAll) { if (fCatchAllEvents == catchAll) return; fCatchAllEvents = catchAll; if (fCatchAllEvents) _InstallEventFilter(); else _RemoveEventFilter(); } status_t StateView::Perform(Command* command) { if (fCommandStack) return fCommandStack->Perform(command); // if there is no command stack, then nobody // else feels responsible... delete command; return B_NO_INIT; } // #pragma mark - bool StateView::_HandleKeyDown(uint32 key, uint32 modifiers) { return false; } bool StateView::_HandleKeyUp(uint32 key, uint32 modifiers) { return false; } void StateView::_InstallEventFilter() { if (!fCatchAllEvents) return; if (!fEventFilter) fEventFilter = new (nothrow) EventFilter(this); if (!fEventFilter || !Window()) return; Window()->AddCommonFilter(fEventFilter); } void StateView::_RemoveEventFilter() { if (!fEventFilter || !Window()) return; Window()->RemoveCommonFilter(fEventFilter); } // _TriggerUpdate void StateView::_TriggerUpdate() { if (fUpdateTarget && fUpdateTarget->Looper()) { fUpdateTarget->Looper()->PostMessage(fUpdateCommand); } }