1 /* 2 * Copyright 2006-2007, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus <superstippi@gmx.de> 7 */ 8 9 #include "StateView.h" 10 11 #include <new> 12 13 #include <Message.h> 14 #include <MessageFilter.h> 15 #include <TextView.h> 16 #include <Window.h> 17 18 #include "Command.h" 19 #include "CommandStack.h" 20 // TODO: hack - somehow figure out of catching 21 // key events for a given control is ok 22 #include "GradientControl.h" 23 #include "ListViews.h" 24 // 25 #include "RWLocker.h" 26 27 using std::nothrow; 28 29 class EventFilter : public BMessageFilter { 30 public: 31 EventFilter(StateView* target) 32 : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE), 33 fTarget(target) 34 { 35 } 36 virtual ~EventFilter() 37 { 38 } 39 virtual filter_result Filter(BMessage* message, BHandler** target) 40 { 41 filter_result result = B_DISPATCH_MESSAGE; 42 switch (message->what) { 43 case B_KEY_DOWN: { 44 if (dynamic_cast<BTextView*>(*target)) 45 break; 46 if (dynamic_cast<SimpleListView*>(*target)) 47 break; 48 if (dynamic_cast<GradientControl*>(*target)) 49 break; 50 uint32 key; 51 uint32 modifiers; 52 if (message->FindInt32("raw_char", (int32*)&key) >= B_OK 53 && message->FindInt32("modifiers", (int32*)&modifiers) >= B_OK) 54 if (fTarget->HandleKeyDown(key, modifiers)) 55 result = B_SKIP_MESSAGE; 56 break; 57 } 58 case B_KEY_UP: { 59 if (dynamic_cast<BTextView*>(*target)) 60 break; 61 if (dynamic_cast<SimpleListView*>(*target)) 62 break; 63 if (dynamic_cast<GradientControl*>(*target)) 64 break; 65 uint32 key; 66 uint32 modifiers; 67 if (message->FindInt32("raw_char", (int32*)&key) >= B_OK 68 && message->FindInt32("modifiers", (int32*)&modifiers) >= B_OK) 69 if (fTarget->HandleKeyUp(key, modifiers)) 70 result = B_SKIP_MESSAGE; 71 break; 72 73 } 74 case B_MODIFIERS_CHANGED: 75 *target = fTarget; 76 break; 77 78 case B_MOUSE_WHEEL_CHANGED: { 79 float x; 80 float y; 81 if (message->FindFloat("be:wheel_delta_x", &x) >= B_OK 82 && message->FindFloat("be:wheel_delta_y", &y) >= B_OK) { 83 if (fTarget->MouseWheelChanged( 84 fTarget->MouseInfo()->position, x, y)) 85 result = B_SKIP_MESSAGE; 86 } 87 break; 88 } 89 default: 90 break; 91 } 92 return result; 93 } 94 private: 95 StateView* fTarget; 96 }; 97 98 // #pragma mark - 99 100 // constructor 101 StateView::StateView(BRect frame, const char* name, 102 uint32 resizingMode, uint32 flags) 103 : BView(frame, name, resizingMode, flags), 104 fCurrentState(NULL), 105 fDropAnticipatingState(NULL), 106 107 fMouseInfo(), 108 109 fCommandStack(NULL), 110 fLocker(NULL), 111 112 fEventFilter(NULL), 113 fCatchAllEvents(false), 114 115 fUpdateTarget(NULL), 116 fUpdateCommand(0) 117 { 118 } 119 120 // destructor 121 StateView::~StateView() 122 { 123 delete fEventFilter; 124 } 125 126 // #pragma mark - 127 128 // AttachedToWindow 129 void 130 StateView::AttachedToWindow() 131 { 132 _InstallEventFilter(); 133 134 BView::AttachedToWindow(); 135 } 136 137 // DetachedFromWindow 138 void 139 StateView::DetachedFromWindow() 140 { 141 _RemoveEventFilter(); 142 143 BView::DetachedFromWindow(); 144 } 145 146 // Draw 147 void 148 StateView::Draw(BRect updateRect) 149 { 150 Draw(this, updateRect); 151 } 152 153 // MessageReceived 154 void 155 StateView::MessageReceived(BMessage* message) 156 { 157 // let the state handle the message if it wants 158 if (fCurrentState) { 159 AutoWriteLocker locker(fLocker); 160 if (fLocker && !locker.IsLocked()) 161 return; 162 163 Command* command = NULL; 164 if (fCurrentState->MessageReceived(message, &command)) { 165 Perform(command); 166 return; 167 } 168 } 169 170 switch (message->what) { 171 case B_MODIFIERS_CHANGED: 172 // NOTE: received only if the view has focus!! 173 if (fCurrentState) { 174 uint32 mods; 175 if (message->FindInt32("modifiers", (int32*)&mods) != B_OK) 176 mods = modifiers(); 177 fCurrentState->ModifiersChanged(mods); 178 fMouseInfo.modifiers = mods; 179 } 180 break; 181 default: 182 BView::MessageReceived(message); 183 } 184 } 185 186 // #pragma mark - 187 188 // MouseDown 189 void 190 StateView::MouseDown(BPoint where) 191 { 192 if (fLocker && !fLocker->WriteLock()) 193 return; 194 195 // query more info from the windows current message if available 196 uint32 buttons; 197 uint32 clicks; 198 BMessage* message = Window() ? Window()->CurrentMessage() : NULL; 199 if (!message || message->FindInt32("buttons", (int32*)&buttons) != B_OK) 200 buttons = B_PRIMARY_MOUSE_BUTTON; 201 if (!message || message->FindInt32("clicks", (int32*)&clicks) != B_OK) 202 clicks = 1; 203 204 if (fCurrentState) 205 fCurrentState->MouseDown(where, buttons, clicks); 206 207 // update mouse info *after* having called the ViewState hook 208 fMouseInfo.buttons = buttons; 209 fMouseInfo.position = where; 210 211 if (fLocker) 212 fLocker->WriteUnlock(); 213 } 214 215 // MouseMoved 216 void 217 StateView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage) 218 { 219 if (fLocker && !fLocker->WriteLock()) 220 return; 221 222 if (dragMessage && !fDropAnticipatingState) { 223 // switch to a drop anticipating state if there is one available 224 fDropAnticipatingState = StateForDragMessage(dragMessage); 225 if (fDropAnticipatingState) 226 fDropAnticipatingState->Init(); 227 } 228 229 // TODO: I don't like this too much 230 if (!dragMessage && fDropAnticipatingState) { 231 fDropAnticipatingState->Cleanup(); 232 fDropAnticipatingState = NULL; 233 } 234 235 if (fDropAnticipatingState) 236 fDropAnticipatingState->MouseMoved(where, transit, dragMessage); 237 else { 238 if (fCurrentState) { 239 fCurrentState->MouseMoved(where, transit, dragMessage); 240 if (fMouseInfo.buttons != 0) 241 _TriggerUpdate(); 242 } 243 } 244 245 // update mouse info *after* having called the ViewState hook 246 fMouseInfo.position = where; 247 fMouseInfo.transit = transit; 248 249 if (fLocker) 250 fLocker->WriteUnlock(); 251 } 252 253 // MouseUp 254 void 255 StateView::MouseUp(BPoint where) 256 { 257 if (fLocker && !fLocker->WriteLock()) 258 return; 259 260 if (fDropAnticipatingState) { 261 Perform(fDropAnticipatingState->MouseUp()); 262 fDropAnticipatingState->Cleanup(); 263 fDropAnticipatingState = NULL; 264 265 if (fCurrentState) { 266 fCurrentState->MouseMoved(fMouseInfo.position, fMouseInfo.transit, 267 NULL); 268 } 269 } else { 270 if (fCurrentState) { 271 Perform(fCurrentState->MouseUp()); 272 _TriggerUpdate(); 273 } 274 } 275 276 // update mouse info *after* having called the ViewState hook 277 fMouseInfo.buttons = 0; 278 279 if (fLocker) 280 fLocker->WriteUnlock(); 281 } 282 283 // #pragma mark - 284 285 // KeyDown 286 void 287 StateView::KeyDown(const char* bytes, int32 numBytes) 288 { 289 uint32 key; 290 uint32 modifiers; 291 BMessage* message = Window() ? Window()->CurrentMessage() : NULL; 292 if (message 293 && message->FindInt32("raw_char", (int32*)&key) >= B_OK 294 && message->FindInt32("modifiers", (int32*)&modifiers) >= B_OK) { 295 if (HandleKeyDown(key, modifiers)) 296 return; 297 } 298 BView::KeyDown(bytes, numBytes); 299 } 300 301 // KeyUp 302 void 303 StateView::KeyUp(const char* bytes, int32 numBytes) 304 { 305 uint32 key; 306 uint32 modifiers; 307 BMessage* message = Window() ? Window()->CurrentMessage() : NULL; 308 if (message 309 && message->FindInt32("raw_char", (int32*)&key) >= B_OK 310 && message->FindInt32("modifiers", (int32*)&modifiers) >= B_OK) { 311 if (HandleKeyUp(key, modifiers)) 312 return; 313 } 314 BView::KeyUp(bytes, numBytes); 315 } 316 317 318 // #pragma mark - 319 320 321 status_t 322 StateView::Perform(perform_code code, void* data) 323 { 324 return BView::Perform(code, data); 325 } 326 327 328 // #pragma mark - 329 330 // SetState 331 void 332 StateView::SetState(ViewState* state) 333 { 334 if (fCurrentState == state) 335 return; 336 337 // switch states as appropriate 338 if (fCurrentState) 339 fCurrentState->Cleanup(); 340 341 fCurrentState = state; 342 343 if (fCurrentState) 344 fCurrentState->Init(); 345 } 346 347 // UpdateStateCursor 348 void 349 StateView::UpdateStateCursor() 350 { 351 if (!fCurrentState || !fCurrentState->UpdateCursor()) { 352 SetViewCursor(B_CURSOR_SYSTEM_DEFAULT, true); 353 } 354 } 355 356 // Draw 357 void 358 StateView::Draw(BView* into, BRect updateRect) 359 { 360 if (fLocker && !fLocker->ReadLock()) { 361 return; 362 } 363 364 if (fCurrentState) 365 fCurrentState->Draw(into, updateRect); 366 367 if (fDropAnticipatingState) 368 fDropAnticipatingState->Draw(into, updateRect); 369 370 if (fLocker) 371 fLocker->ReadUnlock(); 372 } 373 374 // MouseWheelChanged 375 bool 376 StateView::MouseWheelChanged(BPoint where, float x, float y) 377 { 378 return false; 379 } 380 381 // HandleKeyDown 382 bool 383 StateView::HandleKeyDown(uint32 key, uint32 modifiers) 384 { 385 // down't allow key events if mouse already pressed 386 // (central place to prevent command stack mix up) 387 if (fMouseInfo.buttons != 0) 388 return false; 389 390 AutoWriteLocker locker(fLocker); 391 if (fLocker && !locker.IsLocked()) 392 return false; 393 394 if (_HandleKeyDown(key, modifiers)) 395 return true; 396 397 if (fCurrentState) { 398 Command* command = NULL; 399 if (fCurrentState->HandleKeyDown(key, modifiers, &command)) { 400 Perform(command); 401 return true; 402 } 403 } 404 return false; 405 } 406 407 // HandleKeyUp 408 bool 409 StateView::HandleKeyUp(uint32 key, uint32 modifiers) 410 { 411 // down't allow key events if mouse already pressed 412 // (central place to prevent command stack mix up) 413 if (fMouseInfo.buttons != 0) 414 return false; 415 416 AutoWriteLocker locker(fLocker); 417 if (fLocker && !locker.IsLocked()) 418 return false; 419 420 if (_HandleKeyUp(key, modifiers)) 421 return true; 422 423 if (fCurrentState) { 424 Command* command = NULL; 425 if (fCurrentState->HandleKeyUp(key, modifiers, &command)) { 426 Perform(command); 427 return true; 428 } 429 } 430 return false; 431 } 432 433 // FilterMouse 434 void 435 StateView::FilterMouse(BPoint* where) const 436 { 437 } 438 439 // StateForDragMessage 440 ViewState* 441 StateView::StateForDragMessage(const BMessage* message) 442 { 443 return NULL; 444 } 445 446 // SetCommandStack 447 void 448 StateView::SetCommandStack(::CommandStack* stack) 449 { 450 fCommandStack = stack; 451 } 452 453 // SetLocker 454 void 455 StateView::SetLocker(RWLocker* locker) 456 { 457 fLocker = locker; 458 } 459 460 // SetUpdateTarget 461 void 462 StateView::SetUpdateTarget(BHandler* target, uint32 command) 463 { 464 fUpdateTarget = target; 465 fUpdateCommand = command; 466 } 467 468 // SetCatchAllEvents 469 void 470 StateView::SetCatchAllEvents(bool catchAll) 471 { 472 if (fCatchAllEvents == catchAll) 473 return; 474 475 fCatchAllEvents = catchAll; 476 477 if (fCatchAllEvents) 478 _InstallEventFilter(); 479 else 480 _RemoveEventFilter(); 481 } 482 483 // Perform 484 status_t 485 StateView::Perform(Command* command) 486 { 487 if (fCommandStack) 488 return fCommandStack->Perform(command); 489 490 // if there is no command stack, then nobody 491 // else feels responsible... 492 delete command; 493 494 return B_NO_INIT; 495 } 496 497 // #pragma mark - 498 499 // _HandleKeyDown 500 bool 501 StateView::_HandleKeyDown(uint32 key, uint32 modifiers) 502 { 503 return false; 504 } 505 506 // _HandleKeyUp 507 bool 508 StateView::_HandleKeyUp(uint32 key, uint32 modifiers) 509 { 510 return false; 511 } 512 513 // _InstallEventFilter 514 void 515 StateView::_InstallEventFilter() 516 { 517 if (!fCatchAllEvents) 518 return; 519 520 if (!fEventFilter) 521 fEventFilter = new (nothrow) EventFilter(this); 522 523 if (!fEventFilter || !Window()) 524 return; 525 526 Window()->AddCommonFilter(fEventFilter); 527 } 528 529 void 530 StateView::_RemoveEventFilter() 531 { 532 if (!fEventFilter || !Window()) 533 return; 534 535 Window()->RemoveCommonFilter(fEventFilter); 536 } 537 538 // _TriggerUpdate 539 void 540 StateView::_TriggerUpdate() 541 { 542 if (fUpdateTarget && fUpdateTarget->Looper()) { 543 fUpdateTarget->Looper()->PostMessage(fUpdateCommand); 544 } 545 } 546