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