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 22using namespace BPrivate; 23 24 25// #pragma mark - 26 27 28SATWindow::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 55SATWindow::~SATWindow() 56{ 57 if (fWindowArea != NULL) 58 fWindowArea->Group()->RemoveWindow(this); 59} 60 61 62SATDecorator* 63SATWindow::GetDecorator() const 64{ 65 return static_cast<SATDecorator*>(fWindow->Decorator()); 66} 67 68 69SATGroup* 70SATWindow::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 102bool 103SATWindow::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 115bool 116SATWindow::PropagateToGroup(SATGroup* group) 117{ 118 if (fWindowArea == NULL) 119 return false; 120 return fWindowArea->PropagateToGroup(group); 121} 122 123 124bool 125SATWindow::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 134bool 135SATWindow::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 return true; 145} 146 147 148bool 149SATWindow::StackWindow(SATWindow* child) 150{ 151 SATGroup* group = GetGroup(); 152 WindowArea* area = GetWindowArea(); 153 if (!group || !area) 154 return false; 155 156 if (group->AddWindow(child, area, this) == false) 157 return false; 158 159 DoGroupLayout(); 160 161 if (fWindow->AddWindowToStack(child->GetWindow()) == false) { 162 group->RemoveWindow(child); 163 DoGroupLayout(); 164 return false; 165 } 166 167 return true; 168} 169 170 171void 172SATWindow::RemovedFromArea(WindowArea* area) 173{ 174 SATDecorator* decorator = GetDecorator(); 175 if (decorator != NULL) 176 fOldTabLocatiom = decorator->TabRect(fWindow->PositionInStack()).left; 177 178 fWindow->DetachFromWindowStack(true); 179 for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++) 180 fSATSnappingBehaviourList.ItemAt(i)->RemovedFromArea(area); 181 182 fWindowArea = NULL; 183} 184 185 186void 187SATWindow::WindowLookChanged(window_look look) 188{ 189 for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++) 190 fSATSnappingBehaviourList.ItemAt(i)->WindowLookChanged(look); 191} 192 193 194void 195SATWindow::FindSnappingCandidates() 196{ 197 fOngoingSnapping = NULL; 198 199 if (fWindow->Feel() != B_NORMAL_WINDOW_FEEL) 200 return; 201 202 GroupIterator groupIterator(fStackAndTile, GetWindow()->Desktop()); 203 for (SATGroup* group = groupIterator.NextGroup(); group; 204 group = groupIterator.NextGroup()) { 205 if (group->CountItems() == 1 206 && group->WindowAt(0)->GetWindow()->Feel() != B_NORMAL_WINDOW_FEEL) 207 continue; 208 for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++) { 209 if (fSATSnappingBehaviourList.ItemAt(i)->FindSnappingCandidates( 210 group)) { 211 fOngoingSnapping = fSATSnappingBehaviourList.ItemAt(i); 212 return; 213 } 214 } 215 } 216} 217 218 219bool 220SATWindow::JoinCandidates() 221{ 222 if (!fOngoingSnapping) 223 return false; 224 bool status = fOngoingSnapping->JoinCandidates(); 225 fOngoingSnapping = NULL; 226 227 return status; 228} 229 230 231void 232SATWindow::DoGroupLayout() 233{ 234 if (!PositionManagedBySAT()) 235 return; 236 237 if (fWindowArea != NULL) 238 fWindowArea->DoGroupLayout(); 239} 240 241 242void 243SATWindow::AdjustSizeLimits(BRect targetFrame) 244{ 245 SATDecorator* decorator = GetDecorator(); 246 if (decorator == NULL) 247 return; 248 249 targetFrame.right -= 2 * (int32)decorator->BorderWidth(); 250 targetFrame.bottom -= 2 * (int32)decorator->BorderWidth() 251 + (int32)decorator->TabHeight() + 1; 252 253 int32 minWidth, maxWidth; 254 int32 minHeight, maxHeight; 255 GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight); 256 257 if (maxWidth < targetFrame.Width()) 258 maxWidth = targetFrame.IntegerWidth(); 259 if (maxHeight < targetFrame.Height()) 260 maxHeight = targetFrame.IntegerHeight(); 261 262 fWindow->SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight); 263} 264 265 266void 267SATWindow::GetSizeLimits(int32* minWidth, int32* maxWidth, int32* minHeight, 268 int32* maxHeight) const 269{ 270 *minWidth = fOriginalMinWidth; 271 *minHeight = fOriginalMinHeight; 272 *maxWidth = fOriginalMaxWidth; 273 *maxHeight = fOriginalMaxHeight; 274 275 SATDecorator* decorator = GetDecorator(); 276 if (decorator == NULL) 277 return; 278 279 int32 minDecorWidth = 1, maxDecorWidth = 1; 280 int32 minDecorHeight = 1, maxDecorHeight = 1; 281 decorator->GetSizeLimits(&minDecorWidth, &minDecorHeight, 282 &maxDecorWidth, &maxDecorHeight); 283 284 // if no size limit is set but the window is not resizeable choose the 285 // current size as limit 286 if (IsHResizeable() == false && fOriginalMinWidth <= minDecorWidth) 287 *minWidth = (int32)fOriginalWidth; 288 if (IsVResizeable() == false && fOriginalMinHeight <= minDecorHeight) 289 *minHeight = (int32)fOriginalHeight; 290 291 if (*minWidth > *maxWidth) 292 *maxWidth = *minWidth; 293 if (*minHeight > *maxHeight) 294 *maxHeight = *minHeight; 295} 296 297 298void 299SATWindow::AddDecorator(int32* minWidth, int32* maxWidth, int32* minHeight, 300 int32* maxHeight) 301{ 302 SATDecorator* decorator = GetDecorator(); 303 if (decorator == NULL) 304 return; 305 306 *minWidth += 2 * (int32)decorator->BorderWidth(); 307 *minHeight += 2 * (int32)decorator->BorderWidth() 308 + (int32)decorator->TabHeight() + 1; 309 *maxWidth += 2 * (int32)decorator->BorderWidth(); 310 *maxHeight += 2 * (int32)decorator->BorderWidth() 311 + (int32)decorator->TabHeight() + 1; 312} 313 314 315void 316SATWindow::AddDecorator(BRect& frame) 317{ 318 SATDecorator* decorator = GetDecorator(); 319 if (!decorator) 320 return; 321 frame.left -= decorator->BorderWidth(); 322 frame.right += decorator->BorderWidth() + 1; 323 frame.top -= decorator->BorderWidth() + decorator->TabHeight() + 1; 324 frame.bottom += decorator->BorderWidth(); 325} 326 327 328void 329SATWindow::SetOriginalSizeLimits(int32 minWidth, int32 maxWidth, 330 int32 minHeight, int32 maxHeight) 331{ 332 fOriginalMinWidth = minWidth; 333 fOriginalMaxWidth = maxWidth; 334 fOriginalMinHeight = minHeight; 335 fOriginalMaxHeight = maxHeight; 336 337 if (fWindowArea != NULL) 338 fWindowArea->UpdateSizeLimits(); 339} 340 341 342void 343SATWindow::Resized() 344{ 345 bool hResizeable = IsHResizeable(); 346 bool vResizeable = IsVResizeable(); 347 if (hResizeable == false && vResizeable == false) 348 return; 349 350 BRect frame = fWindow->Frame(); 351 if (hResizeable) 352 fOriginalWidth = frame.Width(); 353 if (vResizeable) 354 fOriginalHeight = frame.Height(); 355 356 if (fWindowArea != NULL) 357 fWindowArea->UpdateSizeConstaints(CompleteWindowFrame()); 358} 359 360 361bool 362SATWindow::IsHResizeable() const 363{ 364 if (fWindow->Look() == B_MODAL_WINDOW_LOOK 365 || fWindow->Look() == B_BORDERED_WINDOW_LOOK 366 || fWindow->Look() == B_NO_BORDER_WINDOW_LOOK 367 || (fWindow->Flags() & B_NOT_RESIZABLE) != 0 368 || (fWindow->Flags() & B_NOT_H_RESIZABLE) != 0) 369 return false; 370 return true; 371} 372 373 374bool 375SATWindow::IsVResizeable() const 376{ 377 if (fWindow->Look() == B_MODAL_WINDOW_LOOK 378 || fWindow->Look() == B_BORDERED_WINDOW_LOOK 379 || fWindow->Look() == B_NO_BORDER_WINDOW_LOOK 380 || (fWindow->Flags() & B_NOT_RESIZABLE) != 0 381 || (fWindow->Flags() & B_NOT_V_RESIZABLE) != 0) 382 return false; 383 return true; 384} 385 386 387BRect 388SATWindow::CompleteWindowFrame() 389{ 390 BRect frame = fWindow->Frame(); 391 if (fDesktop 392 && fDesktop->CurrentWorkspace() != fWindow->CurrentWorkspace()) { 393 window_anchor& anchor = fWindow->Anchor(fWindow->CurrentWorkspace()); 394 if (anchor.position != kInvalidWindowPosition) 395 frame.OffsetTo(anchor.position); 396 } 397 398 AddDecorator(frame); 399 return frame; 400} 401 402 403bool 404SATWindow::PositionManagedBySAT() 405{ 406 if (fWindowArea == NULL || fWindowArea->Group()->CountItems() == 1) 407 return false; 408 409 return true; 410} 411 412 413bool 414SATWindow::HighlightTab(bool active) 415{ 416 SATDecorator* decorator = GetDecorator(); 417 if (!decorator) 418 return false; 419 420 int32 tabIndex = fWindow->PositionInStack(); 421 BRegion dirty; 422 uint8 highlight = active ? SATDecorator::HIGHLIGHT_STACK_AND_TILE : 0; 423 decorator->SetRegionHighlight(Decorator::REGION_TAB, highlight, &dirty, 424 tabIndex); 425 decorator->SetRegionHighlight(Decorator::REGION_CLOSE_BUTTON, highlight, 426 &dirty, tabIndex); 427 decorator->SetRegionHighlight(Decorator::REGION_ZOOM_BUTTON, highlight, 428 &dirty, tabIndex); 429 430 fWindow->TopLayerStackWindow()->ProcessDirtyRegion(dirty); 431 return true; 432} 433 434 435bool 436SATWindow::HighlightBorders(Decorator::Region region, bool active) 437{ 438 SATDecorator* decorator = GetDecorator(); 439 if (!decorator) 440 return false; 441 442 BRegion dirty; 443 uint8 highlight = active ? SATDecorator::HIGHLIGHT_STACK_AND_TILE : 0; 444 decorator->SetRegionHighlight(region, highlight, &dirty); 445 446 fWindow->ProcessDirtyRegion(dirty); 447 return true; 448} 449 450 451uint64 452SATWindow::Id() 453{ 454 return fId; 455} 456 457 458bool 459SATWindow::SetSettings(const BMessage& message) 460{ 461 uint64 id; 462 if (message.FindInt64("window_id", (int64*)&id) != B_OK) 463 return false; 464 fId = id; 465 return true; 466} 467 468 469void 470SATWindow::GetSettings(BMessage& message) 471{ 472 message.AddInt64("window_id", fId); 473} 474 475 476uint64 477SATWindow::_GenerateId() 478{ 479 bigtime_t time = real_time_clock_usecs(); 480 srand(time); 481 int16 randNumber = rand(); 482 return (time & ~0xFFFF) | randNumber; 483} 484 485 486void 487SATWindow::_RestoreOriginalSize(bool stayBelowMouse) 488{ 489 // restore size 490 fWindow->SetSizeLimits(fOriginalMinWidth, fOriginalMaxWidth, 491 fOriginalMinHeight, fOriginalMaxHeight); 492 BRect frame = fWindow->Frame(); 493 float x = 0, y = 0; 494 if (IsHResizeable() == false) 495 x = fOriginalWidth - frame.Width(); 496 if (IsVResizeable() == false) 497 y = fOriginalHeight - frame.Height(); 498 fDesktop->ResizeWindowBy(fWindow, x, y); 499 500 if (!stayBelowMouse) 501 return; 502 // verify that the window stays below the mouse 503 BPoint mousePosition; 504 int32 buttons; 505 fDesktop->GetLastMouseState(&mousePosition, &buttons); 506 SATDecorator* decorator = GetDecorator(); 507 if (decorator == NULL) 508 return; 509 BRect tabRect = decorator->TitleBarRect(); 510 if (mousePosition.y < tabRect.bottom && mousePosition.y > tabRect.top 511 && mousePosition.x <= frame.right + decorator->BorderWidth() +1 512 && mousePosition.x >= frame.left + decorator->BorderWidth()) { 513 // verify mouse stays on the tab 514 float oldOffset = mousePosition.x - fOldTabLocatiom; 515 float deltaX = mousePosition.x - (tabRect.left + oldOffset); 516 fDesktop->MoveWindowBy(fWindow, deltaX, 0); 517 } else { 518 // verify mouse stays on the border 519 float deltaX = 0; 520 float deltaY = 0; 521 BRect newFrame = fWindow->Frame(); 522 if (x != 0 && mousePosition.x > frame.left 523 && mousePosition.x > newFrame.right) { 524 deltaX = mousePosition.x - newFrame.right; 525 if (mousePosition.x > frame.right) 526 deltaX -= mousePosition.x - frame.right; 527 } 528 if (y != 0 && mousePosition.y > frame.top 529 && mousePosition.y > newFrame.bottom) { 530 deltaY = mousePosition.y - newFrame.bottom; 531 if (mousePosition.y > frame.bottom) 532 deltaY -= mousePosition.y - frame.bottom; 533 } 534 fDesktop->MoveWindowBy(fWindow, deltaX, deltaY); 535 } 536} 537