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