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 // #pragma mark - 318 319 // SetState 320 void 321 StateView::SetState(ViewState* state) 322 { 323 if (fCurrentState == state) 324 return; 325 326 // switch states as appropriate 327 if (fCurrentState) 328 fCurrentState->Cleanup(); 329 330 fCurrentState = state; 331 332 if (fCurrentState) 333 fCurrentState->Init(); 334 } 335 336 // UpdateStateCursor 337 void 338 StateView::UpdateStateCursor() 339 { 340 if (!fCurrentState || !fCurrentState->UpdateCursor()) { 341 SetViewCursor(B_CURSOR_SYSTEM_DEFAULT, true); 342 } 343 } 344 345 // Draw 346 void 347 StateView::Draw(BView* into, BRect updateRect) 348 { 349 if (fLocker && !fLocker->ReadLock()) { 350 return; 351 } 352 353 if (fCurrentState) 354 fCurrentState->Draw(into, updateRect); 355 356 if (fDropAnticipatingState) 357 fDropAnticipatingState->Draw(into, updateRect); 358 359 if (fLocker) 360 fLocker->ReadUnlock(); 361 } 362 363 // MouseWheelChanged 364 bool 365 StateView::MouseWheelChanged(BPoint where, float x, float y) 366 { 367 return false; 368 } 369 370 // HandleKeyDown 371 bool 372 StateView::HandleKeyDown(uint32 key, uint32 modifiers) 373 { 374 // down't allow key events if mouse already pressed 375 // (central place to prevent command stack mix up) 376 if (fMouseInfo.buttons != 0) 377 return false; 378 379 AutoWriteLocker locker(fLocker); 380 if (fLocker && !locker.IsLocked()) 381 return false; 382 383 if (_HandleKeyDown(key, modifiers)) 384 return true; 385 386 if (fCurrentState) { 387 Command* command = NULL; 388 if (fCurrentState->HandleKeyDown(key, modifiers, &command)) { 389 Perform(command); 390 return true; 391 } 392 } 393 return false; 394 } 395 396 // HandleKeyUp 397 bool 398 StateView::HandleKeyUp(uint32 key, uint32 modifiers) 399 { 400 // down't allow key events if mouse already pressed 401 // (central place to prevent command stack mix up) 402 if (fMouseInfo.buttons != 0) 403 return false; 404 405 AutoWriteLocker locker(fLocker); 406 if (fLocker && !locker.IsLocked()) 407 return false; 408 409 if (_HandleKeyUp(key, modifiers)) 410 return true; 411 412 if (fCurrentState) { 413 Command* command = NULL; 414 if (fCurrentState->HandleKeyUp(key, modifiers, &command)) { 415 Perform(command); 416 return true; 417 } 418 } 419 return false; 420 } 421 422 // FilterMouse 423 void 424 StateView::FilterMouse(BPoint* where) const 425 { 426 } 427 428 // StateForDragMessage 429 ViewState* 430 StateView::StateForDragMessage(const BMessage* message) 431 { 432 return NULL; 433 } 434 435 // SetCommandStack 436 void 437 StateView::SetCommandStack(::CommandStack* stack) 438 { 439 fCommandStack = stack; 440 } 441 442 // SetLocker 443 void 444 StateView::SetLocker(RWLocker* locker) 445 { 446 fLocker = locker; 447 } 448 449 // SetUpdateTarget 450 void 451 StateView::SetUpdateTarget(BHandler* target, uint32 command) 452 { 453 fUpdateTarget = target; 454 fUpdateCommand = command; 455 } 456 457 // SetCatchAllEvents 458 void 459 StateView::SetCatchAllEvents(bool catchAll) 460 { 461 if (fCatchAllEvents == catchAll) 462 return; 463 464 fCatchAllEvents = catchAll; 465 466 if (fCatchAllEvents) 467 _InstallEventFilter(); 468 else 469 _RemoveEventFilter(); 470 } 471 472 // Perform 473 status_t 474 StateView::Perform(Command* command) 475 { 476 if (fCommandStack) 477 return fCommandStack->Perform(command); 478 479 // if there is no command stack, then nobody 480 // else feels responsible... 481 delete command; 482 483 return B_NO_INIT; 484 } 485 486 // #pragma mark - 487 488 // _HandleKeyDown 489 bool 490 StateView::_HandleKeyDown(uint32 key, uint32 modifiers) 491 { 492 return false; 493 } 494 495 // _HandleKeyUp 496 bool 497 StateView::_HandleKeyUp(uint32 key, uint32 modifiers) 498 { 499 return false; 500 } 501 502 // _InstallEventFilter 503 void 504 StateView::_InstallEventFilter() 505 { 506 if (!fCatchAllEvents) 507 return; 508 509 if (!fEventFilter) 510 fEventFilter = new (nothrow) EventFilter(this); 511 512 if (!fEventFilter || !Window()) 513 return; 514 515 Window()->AddCommonFilter(fEventFilter); 516 } 517 518 void 519 StateView::_RemoveEventFilter() 520 { 521 if (!fEventFilter || !Window()) 522 return; 523 524 Window()->RemoveCommonFilter(fEventFilter); 525 } 526 527 // _TriggerUpdate 528 void 529 StateView::_TriggerUpdate() 530 { 531 if (fUpdateTarget && fUpdateTarget->Looper()) { 532 fUpdateTarget->Looper()->PostMessage(fUpdateCommand); 533 } 534 } 535