1 /* 2 * Copyright 2010, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Clemens Zeidler <haiku@clemens-zeidler.de> 7 */ 8 9 10 #include "SATWindow.h" 11 12 #include <Debug.h> 13 14 #include "StackAndTilePrivate.h" 15 16 #include "Desktop.h" 17 #include "SATGroup.h" 18 #include "ServerApp.h" 19 #include "Window.h" 20 21 22 using namespace BPrivate; 23 24 25 // #pragma mark - 26 27 28 SATWindow::SATWindow(StackAndTile* sat, Window* window) 29 : 30 fWindow(window), 31 fStackAndTile(sat), 32 33 fWindowArea(NULL), 34 35 fOngoingSnapping(NULL), 36 fSATStacking(this), 37 fSATTiling(this) 38 { 39 fId = _GenerateId(); 40 41 fDesktop = fWindow->Desktop(); 42 43 // read initial limit values 44 fWindow->GetSizeLimits(&fOriginalMinWidth, &fOriginalMaxWidth, 45 &fOriginalMinHeight, &fOriginalMaxHeight); 46 BRect frame = fWindow->Frame(); 47 fOriginalWidth = frame.Width(); 48 fOriginalHeight = frame.Height(); 49 50 fSATSnappingBehaviourList.AddItem(&fSATStacking); 51 fSATSnappingBehaviourList.AddItem(&fSATTiling); 52 } 53 54 55 SATWindow::~SATWindow() 56 { 57 if (fWindowArea != NULL) 58 fWindowArea->Group()->RemoveWindow(this); 59 } 60 61 62 SATDecorator* 63 SATWindow::GetDecorator() const 64 { 65 return static_cast<SATDecorator*>(fWindow->Decorator()); 66 } 67 68 69 SATGroup* 70 SATWindow::GetGroup() 71 { 72 if (fWindowArea == NULL) { 73 SATGroup* group = new (std::nothrow)SATGroup; 74 if (group == NULL) 75 return group; 76 BReference<SATGroup> groupRef; 77 groupRef.SetTo(group, true); 78 79 /* AddWindow also will trigger the window to hold a reference on the new 80 group. */ 81 if (group->AddWindow(this, NULL, NULL, NULL, NULL) == false) 82 return NULL; 83 } 84 85 ASSERT(fWindowArea != NULL); 86 87 // manually set the tabs of the single window 88 if (PositionManagedBySAT() == false) { 89 BRect frame = CompleteWindowFrame(); 90 fWindowArea->LeftTopCrossing()->VerticalTab()->SetPosition(frame.left); 91 fWindowArea->LeftTopCrossing()->HorizontalTab()->SetPosition(frame.top); 92 fWindowArea->RightBottomCrossing()->VerticalTab()->SetPosition( 93 frame.right); 94 fWindowArea->RightBottomCrossing()->HorizontalTab()->SetPosition( 95 frame.bottom); 96 } 97 98 return fWindowArea->Group(); 99 } 100 101 102 bool 103 SATWindow::HandleMessage(SATWindow* sender, BPrivate::LinkReceiver& link, 104 BPrivate::LinkSender& reply) 105 { 106 int32 target; 107 link.Read<int32>(&target); 108 if (target == kStacking) 109 return StackingEventHandler::HandleMessage(sender, link, reply); 110 111 return false; 112 } 113 114 115 bool 116 SATWindow::PropagateToGroup(SATGroup* group) 117 { 118 if (fWindowArea == NULL) 119 return false; 120 return fWindowArea->PropagateToGroup(group); 121 } 122 123 124 bool 125 SATWindow::AddedToGroup(SATGroup* group, WindowArea* area) 126 { 127 STRACE_SAT("SATWindow::AddedToGroup group: %p window %s\n", group, 128 fWindow->Title()); 129 fWindowArea = area; 130 return true; 131 } 132 133 134 bool 135 SATWindow::RemovedFromGroup(SATGroup* group, bool stayBelowMouse) 136 { 137 STRACE_SAT("SATWindow::RemovedFromGroup group: %p window %s\n", group, 138 fWindow->Title()); 139 140 _RestoreOriginalSize(stayBelowMouse); 141 if (group->CountItems() == 1) 142 group->WindowAt(0)->_RestoreOriginalSize(false); 143 144 fWindowArea = NULL; 145 return true; 146 } 147 148 149 bool 150 SATWindow::StackWindow(SATWindow* child) 151 { 152 SATGroup* group = GetGroup(); 153 WindowArea* area = GetWindowArea(); 154 if (!group || !area) 155 return false; 156 157 if (group->AddWindow(child, area, this) == false) 158 return false; 159 160 DoGroupLayout(); 161 162 if (fWindow->AddWindowToStack(child->GetWindow()) == false) { 163 group->RemoveWindow(child); 164 DoGroupLayout(); 165 return false; 166 } 167 168 return true; 169 } 170 171 172 void 173 SATWindow::RemovedFromArea(WindowArea* area) 174 { 175 SATDecorator* decorator = GetDecorator(); 176 if (decorator != NULL) 177 fOldTabLocatiom = decorator->TabRect(fWindow->PositionInStack()).left; 178 179 fWindow->DetachFromWindowStack(true); 180 for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++) 181 fSATSnappingBehaviourList.ItemAt(i)->RemovedFromArea(area); 182 } 183 184 185 void 186 SATWindow::WindowLookChanged(window_look look) 187 { 188 for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++) 189 fSATSnappingBehaviourList.ItemAt(i)->WindowLookChanged(look); 190 } 191 192 193 void 194 SATWindow::FindSnappingCandidates() 195 { 196 fOngoingSnapping = NULL; 197 198 if (fWindow->Feel() != B_NORMAL_WINDOW_FEEL) 199 return; 200 201 GroupIterator groupIterator(fStackAndTile, GetWindow()->Desktop()); 202 for (SATGroup* group = groupIterator.NextGroup(); group; 203 group = groupIterator.NextGroup()) { 204 if (group->CountItems() == 1 205 && group->WindowAt(0)->GetWindow()->Feel() != B_NORMAL_WINDOW_FEEL) 206 continue; 207 for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++) { 208 if (fSATSnappingBehaviourList.ItemAt(i)->FindSnappingCandidates( 209 group)) { 210 fOngoingSnapping = fSATSnappingBehaviourList.ItemAt(i); 211 return; 212 } 213 } 214 } 215 } 216 217 218 bool 219 SATWindow::JoinCandidates() 220 { 221 if (!fOngoingSnapping) 222 return false; 223 bool status = fOngoingSnapping->JoinCandidates(); 224 fOngoingSnapping = NULL; 225 226 return status; 227 } 228 229 230 void 231 SATWindow::DoGroupLayout() 232 { 233 if (!PositionManagedBySAT()) 234 return; 235 236 if (fWindowArea != NULL) 237 fWindowArea->DoGroupLayout(); 238 } 239 240 241 void 242 SATWindow::AdjustSizeLimits(BRect targetFrame) 243 { 244 SATDecorator* decorator = GetDecorator(); 245 if (decorator == NULL) 246 return; 247 248 targetFrame.right -= 2 * (int32)decorator->BorderWidth(); 249 targetFrame.bottom -= 2 * (int32)decorator->BorderWidth() 250 + (int32)decorator->TabHeight() + 1; 251 252 int32 minWidth, maxWidth; 253 int32 minHeight, maxHeight; 254 GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight); 255 256 if (maxWidth < targetFrame.Width()) 257 maxWidth = targetFrame.IntegerWidth(); 258 if (maxHeight < targetFrame.Height()) 259 maxHeight = targetFrame.IntegerHeight(); 260 261 fWindow->SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight); 262 } 263 264 265 void 266 SATWindow::GetSizeLimits(int32* minWidth, int32* maxWidth, int32* minHeight, 267 int32* maxHeight) const 268 { 269 *minWidth = fOriginalMinWidth; 270 *minHeight = fOriginalMinHeight; 271 *maxWidth = fOriginalMaxWidth; 272 *maxHeight = fOriginalMaxHeight; 273 274 SATDecorator* decorator = GetDecorator(); 275 if (decorator == NULL) 276 return; 277 278 int32 minDecorWidth = 1, maxDecorWidth = 1; 279 int32 minDecorHeight = 1, maxDecorHeight = 1; 280 decorator->GetSizeLimits(&minDecorWidth, &minDecorHeight, 281 &maxDecorWidth, &maxDecorHeight); 282 283 // if no size limit is set but the window is not resizeable choose the 284 // current size as limit 285 if (IsHResizeable() == false && fOriginalMinWidth <= minDecorWidth) 286 *minWidth = (int32)fOriginalWidth; 287 if (IsVResizeable() == false && fOriginalMinHeight <= minDecorHeight) 288 *minHeight = (int32)fOriginalHeight; 289 290 if (*minWidth > *maxWidth) 291 *maxWidth = *minWidth; 292 if (*minHeight > *maxHeight) 293 *maxHeight = *minHeight; 294 } 295 296 297 void 298 SATWindow::AddDecorator(int32* minWidth, int32* maxWidth, int32* minHeight, 299 int32* maxHeight) 300 { 301 SATDecorator* decorator = GetDecorator(); 302 if (decorator == NULL) 303 return; 304 305 *minWidth += 2 * (int32)decorator->BorderWidth(); 306 *minHeight += 2 * (int32)decorator->BorderWidth() 307 + (int32)decorator->TabHeight() + 1; 308 *maxWidth += 2 * (int32)decorator->BorderWidth(); 309 *maxHeight += 2 * (int32)decorator->BorderWidth() 310 + (int32)decorator->TabHeight() + 1; 311 } 312 313 314 void 315 SATWindow::AddDecorator(BRect& frame) 316 { 317 SATDecorator* decorator = GetDecorator(); 318 if (!decorator) 319 return; 320 frame.left -= decorator->BorderWidth(); 321 frame.right += decorator->BorderWidth() + 1; 322 frame.top -= decorator->BorderWidth() + decorator->TabHeight() + 1; 323 frame.bottom += decorator->BorderWidth(); 324 } 325 326 327 void 328 SATWindow::SetOriginalSizeLimits(int32 minWidth, int32 maxWidth, 329 int32 minHeight, int32 maxHeight) 330 { 331 fOriginalMinWidth = minWidth; 332 fOriginalMaxWidth = maxWidth; 333 fOriginalMinHeight = minHeight; 334 fOriginalMaxHeight = maxHeight; 335 336 if (fWindowArea != NULL) 337 fWindowArea->UpdateSizeLimits(); 338 } 339 340 341 void 342 SATWindow::Resized() 343 { 344 bool hResizeable = IsHResizeable(); 345 bool vResizeable = IsVResizeable(); 346 if (hResizeable == false && vResizeable == false) 347 return; 348 349 BRect frame = fWindow->Frame(); 350 if (hResizeable) 351 fOriginalWidth = frame.Width(); 352 if (vResizeable) 353 fOriginalHeight = frame.Height(); 354 355 if (fWindowArea != NULL) 356 fWindowArea->UpdateSizeConstaints(CompleteWindowFrame()); 357 } 358 359 360 bool 361 SATWindow::IsHResizeable() const 362 { 363 if (fWindow->Look() == B_MODAL_WINDOW_LOOK 364 || fWindow->Look() == B_BORDERED_WINDOW_LOOK 365 || fWindow->Look() == B_NO_BORDER_WINDOW_LOOK 366 || (fWindow->Flags() & B_NOT_RESIZABLE) != 0 367 || (fWindow->Flags() & B_NOT_H_RESIZABLE) != 0) 368 return false; 369 return true; 370 } 371 372 373 bool 374 SATWindow::IsVResizeable() const 375 { 376 if (fWindow->Look() == B_MODAL_WINDOW_LOOK 377 || fWindow->Look() == B_BORDERED_WINDOW_LOOK 378 || fWindow->Look() == B_NO_BORDER_WINDOW_LOOK 379 || (fWindow->Flags() & B_NOT_RESIZABLE) != 0 380 || (fWindow->Flags() & B_NOT_V_RESIZABLE) != 0) 381 return false; 382 return true; 383 } 384 385 386 BRect 387 SATWindow::CompleteWindowFrame() 388 { 389 BRect frame = fWindow->Frame(); 390 if (fDesktop 391 && fDesktop->CurrentWorkspace() != fWindow->CurrentWorkspace()) { 392 window_anchor& anchor = fWindow->Anchor(fWindow->CurrentWorkspace()); 393 if (anchor.position != kInvalidWindowPosition) 394 frame.OffsetTo(anchor.position); 395 } 396 397 AddDecorator(frame); 398 return frame; 399 } 400 401 402 bool 403 SATWindow::PositionManagedBySAT() 404 { 405 if (fWindowArea == NULL || fWindowArea->Group()->CountItems() == 1) 406 return false; 407 408 return true; 409 } 410 411 412 bool 413 SATWindow::HighlightTab(bool active) 414 { 415 SATDecorator* decorator = GetDecorator(); 416 if (!decorator) 417 return false; 418 419 int32 tabIndex = fWindow->PositionInStack(); 420 BRegion dirty; 421 uint8 highlight = active ? SATDecorator::HIGHLIGHT_STACK_AND_TILE : 0; 422 decorator->SetRegionHighlight(Decorator::REGION_TAB, highlight, &dirty, 423 tabIndex); 424 decorator->SetRegionHighlight(Decorator::REGION_CLOSE_BUTTON, highlight, 425 &dirty, tabIndex); 426 decorator->SetRegionHighlight(Decorator::REGION_ZOOM_BUTTON, highlight, 427 &dirty, tabIndex); 428 429 fWindow->TopLayerStackWindow()->ProcessDirtyRegion(dirty); 430 return true; 431 } 432 433 434 bool 435 SATWindow::HighlightBorders(Decorator::Region region, bool active) 436 { 437 SATDecorator* decorator = GetDecorator(); 438 if (!decorator) 439 return false; 440 441 BRegion dirty; 442 uint8 highlight = active ? SATDecorator::HIGHLIGHT_STACK_AND_TILE : 0; 443 decorator->SetRegionHighlight(region, highlight, &dirty); 444 445 fWindow->ProcessDirtyRegion(dirty); 446 return true; 447 } 448 449 450 uint64 451 SATWindow::Id() 452 { 453 return fId; 454 } 455 456 457 bool 458 SATWindow::SetSettings(const BMessage& message) 459 { 460 uint64 id; 461 if (message.FindInt64("window_id", (int64*)&id) != B_OK) 462 return false; 463 fId = id; 464 return true; 465 } 466 467 468 void 469 SATWindow::GetSettings(BMessage& message) 470 { 471 message.AddInt64("window_id", fId); 472 } 473 474 475 uint64 476 SATWindow::_GenerateId() 477 { 478 bigtime_t time = real_time_clock_usecs(); 479 srand(time); 480 int16 randNumber = rand(); 481 return (time & ~0xFFFF) | randNumber; 482 } 483 484 485 void 486 SATWindow::_RestoreOriginalSize(bool stayBelowMouse) 487 { 488 // restore size 489 fWindow->SetSizeLimits(fOriginalMinWidth, fOriginalMaxWidth, 490 fOriginalMinHeight, fOriginalMaxHeight); 491 BRect frame = fWindow->Frame(); 492 float x = 0, y = 0; 493 if (IsHResizeable() == false) 494 x = fOriginalWidth - frame.Width(); 495 if (IsVResizeable() == false) 496 y = fOriginalHeight - frame.Height(); 497 fDesktop->ResizeWindowBy(fWindow, x, y); 498 499 if (!stayBelowMouse) 500 return; 501 // verify that the window stays below the mouse 502 BPoint mousePosition; 503 int32 buttons; 504 fDesktop->GetLastMouseState(&mousePosition, &buttons); 505 SATDecorator* decorator = GetDecorator(); 506 if (decorator == NULL) 507 return; 508 BRect tabRect = decorator->TitleBarRect(); 509 if (mousePosition.y < tabRect.bottom && mousePosition.y > tabRect.top 510 && mousePosition.x <= frame.right + decorator->BorderWidth() +1 511 && mousePosition.x >= frame.left + decorator->BorderWidth()) { 512 // verify mouse stays on the tab 513 float oldOffset = mousePosition.x - fOldTabLocatiom; 514 float deltaX = mousePosition.x - (tabRect.left + oldOffset); 515 fDesktop->MoveWindowBy(fWindow, deltaX, 0); 516 } else { 517 // verify mouse stays on the border 518 float deltaX = 0; 519 float deltaY = 0; 520 BRect newFrame = fWindow->Frame(); 521 if (x != 0 && mousePosition.x > frame.left 522 && mousePosition.x > newFrame.right) { 523 deltaX = mousePosition.x - newFrame.right; 524 if (mousePosition.x > frame.right) 525 deltaX -= mousePosition.x - frame.right; 526 } 527 if (y != 0 && mousePosition.y > frame.top 528 && mousePosition.y > newFrame.bottom) { 529 deltaY = mousePosition.y - newFrame.bottom; 530 if (mousePosition.y > frame.bottom) 531 deltaY -= mousePosition.y - frame.bottom; 532 } 533 fDesktop->MoveWindowBy(fWindow, deltaX, deltaY); 534 } 535 } 536