* Copyright 2006-2007, 2023, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
* Zardshard
*/
#include "StateView.h"
#include <new>
#include <Message.h>
#include <MessageFilter.h>
#include <TextView.h>
#include <Window.h>
#include "Command.h"
#include "CommandStack.h"
#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<BTextView*>(*target))
break;
if (dynamic_cast<SimpleListView*>(*target))
break;
if (dynamic_cast<GradientControl*>(*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<BTextView*>(*target))
break;
if (dynamic_cast<SimpleListView*>(*target))
break;
if (dynamic_cast<GradientControl*>(*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;
};
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;
}
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)
{
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:
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);
}
}
void
StateView::MouseDown(BPoint where)
{
if (fLocker && !fLocker->WriteLock())
return;
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);
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) {
fDropAnticipatingState = StateForDragMessage(dragMessage);
if (fDropAnticipatingState)
fDropAnticipatingState->Init();
}
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();
}
}
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();
}
}
fMouseInfo.buttons = 0;
if (fLocker)
fLocker->WriteUnlock();
}
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);
}
status_t
StateView::Perform(perform_code code, void* data)
{
return BView::Perform(code, data);
}
void
StateView::GetPreferredSize(float* width, float* height)
{
if (width != NULL)
*width = fStartingRect.Width();
if (height != NULL)
*height = fStartingRect.Height();
}
void
StateView::SetState(ViewState* state)
{
if (fCurrentState == state)
return;
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)
{
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)
{
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);
delete command;
return B_NO_INIT;
}
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);
}
void
StateView::_TriggerUpdate()
{
if (fUpdateTarget && fUpdateTarget->Looper()) {
fUpdateTarget->Looper()->PostMessage(fUpdateCommand);
}
}