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 #include "Stacking.h" 10 11 #include <Debug.h> 12 13 #include "StackAndTilePrivate.h" 14 15 #include "Desktop.h" 16 #include "SATWindow.h" 17 #include "Window.h" 18 19 20 //#define DEBUG_STACKING 21 22 #ifdef DEBUG_STACKING 23 # define STRACE_STACKING(x...) debug_printf("SAT Stacking: "x) 24 #else 25 # define STRACE_STACKING(x...) ; 26 #endif 27 28 29 using namespace BPrivate; 30 31 32 bool 33 StackingEventHandler::HandleMessage(SATWindow* sender, 34 BPrivate::LinkReceiver& link, BPrivate::LinkSender& reply) 35 { 36 Desktop* desktop = sender->GetDesktop(); 37 StackAndTile* stackAndTile = sender->GetStackAndTile(); 38 39 int32 what; 40 link.Read<int32>(&what); 41 42 switch (what) { 43 case kAddWindowToStack: 44 { 45 port_id port; 46 int32 token; 47 team_id team; 48 link.Read<port_id>(&port); 49 link.Read<int32>(&token); 50 link.Read<team_id>(&team); 51 int32 position; 52 if (link.Read<int32>(&position) != B_OK) 53 return false; 54 55 WindowArea* area = sender->GetWindowArea(); 56 if (!area) 57 return false; 58 if (position < 0) 59 position = area->WindowList().CountItems() - 1; 60 61 SATWindow* parent = area->WindowList().ItemAt(position); 62 Window* window = desktop->WindowForClientLooperPort(port); 63 if (!parent || !window) { 64 reply.StartMessage(B_BAD_VALUE); 65 reply.Flush(); 66 break; 67 } 68 69 SATWindow* candidate = stackAndTile->GetSATWindow(window); 70 if (!candidate) 71 return false; 72 73 // Is that window already part of the stack? 74 if (area->WindowList().HasItem(candidate)) { 75 reply.StartMessage(B_MISMATCHED_VALUES); 76 reply.Flush(); 77 break; 78 } 79 80 if (!parent->StackWindow(candidate)) 81 return false; 82 83 reply.StartMessage(B_OK); 84 reply.Flush(); 85 break; 86 } 87 case kRemoveWindowFromStack: 88 { 89 port_id port; 90 int32 token; 91 team_id team; 92 link.Read<port_id>(&port); 93 link.Read<int32>(&token); 94 if (link.Read<team_id>(&team) != B_OK) 95 return false; 96 97 SATGroup* group = sender->GetGroup(); 98 if (!group) 99 return false; 100 101 Window* window = desktop->WindowForClientLooperPort(port); 102 if (!window) { 103 reply.StartMessage(B_BAD_VALUE); 104 reply.Flush(); 105 break; 106 } 107 SATWindow* candidate = stackAndTile->GetSATWindow(window); 108 if (!candidate) 109 return false; 110 if (!group->RemoveWindow(candidate, false)) 111 return false; 112 break; 113 } 114 case kRemoveWindowFromStackAt: 115 { 116 int32 position; 117 if (link.Read<int32>(&position) != B_OK) 118 return false; 119 SATGroup* group = sender->GetGroup(); 120 WindowArea* area = sender->GetWindowArea(); 121 if (!area || !group) 122 return false; 123 SATWindow* removeWindow = area->WindowList().ItemAt(position); 124 if (!removeWindow) { 125 reply.StartMessage(B_BAD_VALUE); 126 reply.Flush(); 127 break; 128 } 129 130 if (!group->RemoveWindow(removeWindow, false)) 131 return false; 132 133 ServerWindow* window = removeWindow->GetWindow()->ServerWindow(); 134 reply.StartMessage(B_OK); 135 reply.Attach<port_id>(window->ClientLooperPort()); 136 reply.Attach<int32>(window->ClientToken()); 137 reply.Attach<team_id>(window->ClientTeam()); 138 reply.Flush(); 139 break; 140 } 141 case kCountWindowsOnStack: 142 { 143 WindowArea* area = sender->GetWindowArea(); 144 if (!area) 145 return false; 146 reply.StartMessage(B_OK); 147 reply.Attach<int32>(area->WindowList().CountItems()); 148 reply.Flush(); 149 break; 150 } 151 case kWindowOnStackAt: 152 { 153 int32 position; 154 if (link.Read<int32>(&position) != B_OK) 155 return false; 156 WindowArea* area = sender->GetWindowArea(); 157 if (!area) 158 return false; 159 SATWindow* satWindow = area->WindowList().ItemAt(position); 160 if (!satWindow) { 161 reply.StartMessage(B_BAD_VALUE); 162 reply.Flush(); 163 break; 164 } 165 166 ServerWindow* window = satWindow->GetWindow()->ServerWindow(); 167 reply.StartMessage(B_OK); 168 reply.Attach<port_id>(window->ClientLooperPort()); 169 reply.Attach<int32>(window->ClientToken()); 170 reply.Attach<team_id>(window->ClientTeam()); 171 reply.Flush(); 172 break; 173 } 174 case kStackHasWindow: 175 { 176 port_id port; 177 int32 token; 178 team_id team; 179 link.Read<port_id>(&port); 180 link.Read<int32>(&token); 181 if (link.Read<team_id>(&team) != B_OK) 182 return false; 183 184 Window* window = desktop->WindowForClientLooperPort(port); 185 if (!window) { 186 reply.StartMessage(B_BAD_VALUE); 187 reply.Flush(); 188 break; 189 } 190 SATWindow* candidate = stackAndTile->GetSATWindow(window); 191 if (!candidate) 192 return false; 193 194 WindowArea* area = sender->GetWindowArea(); 195 if (!area) 196 return false; 197 reply.StartMessage(B_OK); 198 reply.Attach<bool>(area->WindowList().HasItem(candidate)); 199 reply.Flush(); 200 break; 201 } 202 default: 203 return false; 204 } 205 return true; 206 } 207 208 209 SATStacking::SATStacking(SATWindow* window) 210 : 211 fSATWindow(window), 212 fStackingParent(NULL) 213 { 214 215 } 216 217 218 SATStacking::~SATStacking() 219 { 220 221 } 222 223 224 bool 225 SATStacking::FindSnappingCandidates(SATGroup* group) 226 { 227 _ClearSearchResult(); 228 229 Window* window = fSATWindow->GetWindow(); 230 if (!window->Decorator()) 231 return false; 232 233 BPoint mousePosition; 234 int32 buttons; 235 fSATWindow->GetDesktop()->GetLastMouseState(&mousePosition, &buttons); 236 if (!window->Decorator()->TitleBarRect().Contains(mousePosition)) 237 return false; 238 239 // use the upper edge of the candidate window to find the parent window 240 mousePosition.y = window->Decorator()->TitleBarRect().top; 241 242 for (int i = 0; i < group->CountItems(); i++) { 243 SATWindow* satWindow = group->WindowAt(i); 244 // search for stacking parent 245 Window* parentWindow = satWindow->GetWindow(); 246 if (parentWindow == window || parentWindow->Decorator() == NULL) 247 continue; 248 if (_IsStackableWindow(parentWindow) == false 249 || _IsStackableWindow(window) == false) 250 continue; 251 Decorator::Tab* tab = parentWindow->Decorator()->TabAt( 252 parentWindow->PositionInStack()); 253 if (tab == NULL) 254 continue; 255 if (tab->tabRect.Contains(mousePosition)) { 256 // remember window as the parent for stacking 257 fStackingParent = satWindow; 258 _HighlightWindows(true); 259 return true; 260 } 261 } 262 263 return false; 264 } 265 266 267 bool 268 SATStacking::JoinCandidates() 269 { 270 if (!fStackingParent) 271 return false; 272 273 bool result = fStackingParent->StackWindow(fSATWindow); 274 275 _ClearSearchResult(); 276 return result; 277 } 278 279 280 void 281 SATStacking::RemovedFromArea(WindowArea* area) 282 { 283 const SATWindowList& list = area->WindowList(); 284 if (list.CountItems() > 0) 285 list.ItemAt(0)->DoGroupLayout(); 286 } 287 288 289 void 290 SATStacking::WindowLookChanged(window_look look) 291 { 292 Window* window = fSATWindow->GetWindow(); 293 WindowStack* stack = window->GetWindowStack(); 294 if (stack == NULL) 295 return; 296 SATGroup* group = fSATWindow->GetGroup(); 297 if (group == NULL) 298 return; 299 if (stack->CountWindows() > 1 && _IsStackableWindow(window) == false) 300 group->RemoveWindow(fSATWindow); 301 } 302 303 304 bool 305 SATStacking::_IsStackableWindow(Window* window) 306 { 307 if (window->Look() == B_DOCUMENT_WINDOW_LOOK) 308 return true; 309 if (window->Look() == B_TITLED_WINDOW_LOOK) 310 return true; 311 return false; 312 } 313 314 315 void 316 SATStacking::_ClearSearchResult() 317 { 318 if (!fStackingParent) 319 return; 320 321 _HighlightWindows(false); 322 fStackingParent = NULL; 323 } 324 325 326 void 327 SATStacking::_HighlightWindows(bool highlight) 328 { 329 Desktop* desktop = fSATWindow->GetWindow()->Desktop(); 330 if (!desktop) 331 return; 332 fStackingParent->HighlightTab(highlight); 333 fSATWindow->HighlightTab(highlight); 334 } 335