//------------------------------------------------------------------------------ // Copyright (c) 2001-2002, OpenBeOS // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // // File Name: Workspace.cpp // Author: Adi Oanca // Description: Tracks workspaces // //------------------------------------------------------------------------------ #include #include #include "Workspace.h" #include "Layer.h" #include "WinBorder.h" #include "ServerWindow.h" #include "ServerApp.h" #include "RGBColor.h" #include "Globals.h" #include "FMWList.h" #include "RootLayer.h" #include "Desktop.h" //#define DEBUG_WORKSPACE #ifdef DEBUG_WORKSPACE # include # define STRACE(x) printf x #else # define STRACE(x) ; #endif #ifdef DEBUG_WORKSPACE # include # define STRACESTREAM() PrintToStream() #else # define STRACESTREAM() ; #endif Workspace::Workspace(const uint32 colorspace, int32 ID, const RGBColor& BGColor, RootLayer* owner){ fID = ID; fSpace = colorspace; fBGColor = BGColor; fOwner = owner; fBottomItem = NULL; fTopItem = NULL; fCurrentItem= NULL; fFocusItem = NULL; fFrontItem = NULL; } //--------------------------------------------------------------------------- Workspace::~Workspace(){ //printf("~Workspace( %ld )\n", fID); ListData *toast; while(fBottomItem){ toast = fBottomItem; fBottomItem = fBottomItem->upperItem; delete toast; } fBottomItem = NULL; fTopItem = NULL; fCurrentItem = NULL; fFocusItem = NULL; fFrontItem = NULL; } //--------------------------------------------------------------------------- /* Adds layer ptr to workspace's list of WinBorders. After the item is added * a *smart* search is performed to set the new front WinBorder. Of course, * the first candidate is 'layer', but if it is hidden or it has the * B_AVOID_FRONT flag, surely it won't get that status. * This method also calls SearchAndSetNewFocus witch searches for a WinBorder, * to give focus to, having as preferred 'layer'. * Remember, some windows having B_AVOID_FOCUS can't have the focus state. */ bool Workspace::AddLayerPtr(WinBorder* layer){ // allocate a new item ListData *item; item = new ListData; item->layerPtr = layer; item->upperItem = NULL; item->lowerItem = NULL; opLock.Lock(); // insert 'item' at the end. It doesn't matter where we add it, // it will be placed correctly by SearchAndSetNewFront(item->layerPtr); InsertItem(item, NULL); opLock.Unlock(); STRACE(("\n*AddLayerPtr(%s) -", layer->GetName())); // this may happen in case of subset windows. if(!(layer->IsHidden())){ // do a *smart* search and set the new 'front' SearchAndSetNewFront(layer); // do a *smart* search and set the new 'focus' + a redraw SetFocusLayer(layer); } return true; } /* Removes a WinBorder from workspace's list. It DOES NOT delete it! * If this window was the front/focus one it calls SearchAndSetNew(Front/Focus) * to give the respective state to the window below her. */ //--------------------------------------------------------------------------- bool Workspace::RemoveLayerPtr(WinBorder* layer){ if (!layer) return false; STRACE(("\n*Workspace(%ld)::RemoveLayerPtr(%s)\n", ID(), layer->GetName())); STRACE(("BEFORE ANY opperation:\n")); STRACESTREAM(); // search to see if this workspace has WinBorder's pointer in its list ListData *item = NULL; opLock.Lock(); if ((item = HasItem(layer))){ ListData *nextItem = NULL; bool wasFront = false; bool wasFocus = false; wasFront = FrontLayer() == layer; wasFocus = FocusLayer() == layer; // prepare to set new front/focus if this layer was front/focus nextItem = item->upperItem; if (wasFront) SearchAndSetNewFront(nextItem? nextItem->layerPtr: NULL); // remove some windows. if (item && item->layerPtr->fLevel == B_NORMAL_FEEL) { ListData *listItem = item->lowerItem; while(listItem && (listItem->layerPtr->fLevel == B_FLOATING_SUBSET_FEEL || listItem->layerPtr->fLevel == B_FLOATING_APP_FEEL || listItem->layerPtr->fLevel == B_MODAL_SUBSET_FEEL)) { // *carefuly* remove the item from the list ListData *itemX = listItem; listItem = listItem->lowerItem; RemoveItem(itemX); delete itemX; } } RemoveItem(item); delete item; STRACESTREAM(); opLock.Unlock(); // reset some internal variables layer->SetMainWinBorder(NULL); // its RootLayer is set to NULL by Layer::RemoveChild(layer); STRACE(("Layer %s found and removed from Workspace No %ld\n", layer->GetName(), ID())); if (wasFocus) SetFocusLayer(nextItem? nextItem->layerPtr: NULL); else Invalidate(); return true; } else{ STRACE(("Layer %s NOT found in Workspace No %ld\n", layer->GetName(), ID())); opLock.Unlock(); return false; } } //--------------------------------------------------------------------------- bool Workspace::HideSubsetWindows(WinBorder* layer){ if (!layer) return false; // search to see if this workspace has WinBorder's pointer in its list ListData *item = NULL; opLock.Lock(); if ((item = HasItem(layer))){ ListData *nextItem = NULL; // prepare to set new front/focus if this layer was front/focus nextItem = item->upperItem; SearchAndSetNewFront(nextItem? nextItem->layerPtr: NULL); // we don't care bout focus in this method!!! //SearchAndSetNewFocus(nextItem? nextItem->layerPtr: NULL); // remove some windows. if (item && item->layerPtr->fLevel == B_NORMAL_FEEL) { ListData *listItem = item->lowerItem; while(listItem && (listItem->layerPtr->fLevel == B_FLOATING_SUBSET_FEEL || listItem->layerPtr->fLevel == B_FLOATING_APP_FEEL || listItem->layerPtr->fLevel == B_MODAL_SUBSET_FEEL)) { // *carefuly* remove the item from the list ListData *itemX = listItem; listItem = listItem->lowerItem; RemoveItem(itemX); delete itemX; } } opLock.Unlock(); return true; } else{ opLock.Unlock(); return false; } } //--------------------------------------------------------------------------- WinBorder* Workspace::SetFocusLayer(WinBorder* layer){ STRACE(("\n@Workspace(%ld)::SetFOCUSLayer( %s )\n", ID(), layer? layer->GetName(): "NULL")); if(!(desktop->fGeneralLock.IsLocked())) debugger("Workspace::SetFocusLayer - desktop->fGeneralLock must be LOCKED!\n"); WinBorder *previousFocus = FocusLayer(); SearchAndSetNewFocus(layer); if (previousFocus != FocusLayer()){ if (previousFocus) previousFocus->HighlightDecorator(false); if (FocusLayer()){ FocusLayer()->HighlightDecorator(true); } } STRACE(("\n#Workspace(%ld)::SetFOCUSLayer( %s ) ENDED\n", ID(), layer? layer->GetName(): "NULL")); return FocusLayer(); } //--------------------------------------------------------------------------- WinBorder* Workspace::FocusLayer() const{ return fFocusItem ? fFocusItem->layerPtr : NULL; } //--------------------------------------------------------------------------- WinBorder* Workspace::SetFrontLayer(WinBorder* layer){ STRACE(("\n@Workspace(%ld)::SetFrontLayer( %s )\n", ID(), layer? layer->GetName(): "NULL")); if(!(desktop->fGeneralLock.IsLocked())) debugger("Workspace::SetFRONTLayer - desktop->fGeneralLock must be LOCKED!\n"); SearchAndSetNewFront(layer); STRACESTREAM(); // TODO: remove??? Invalidate(); return fFrontItem? fFrontItem->layerPtr: NULL;; } //--------------------------------------------------------------------------- WinBorder* Workspace::FrontLayer() const{ return fFrontItem? fFrontItem->layerPtr: NULL; } //--------------------------------------------------------------------------- WinBorder* Workspace::GoToBottomItem(){ fCurrentItem = fBottomItem; return fCurrentItem ? fCurrentItem->layerPtr : NULL; } //--------------------------------------------------------------------------- WinBorder* Workspace::GoToUpperItem(){ if (fCurrentItem){ fCurrentItem = fCurrentItem->upperItem; return fCurrentItem ? fCurrentItem->layerPtr : NULL; } else return NULL; } //--------------------------------------------------------------------------- WinBorder* Workspace::GoToTopItem(){ fCurrentItem = fTopItem; return fCurrentItem ? fCurrentItem->layerPtr : NULL; } //--------------------------------------------------------------------------- WinBorder* Workspace::GoToLowerItem(){ if (fCurrentItem){ fCurrentItem = fCurrentItem->lowerItem; return fCurrentItem ? fCurrentItem->layerPtr : NULL; } else return NULL; } //--------------------------------------------------------------------------- bool Workspace::GoToItem(WinBorder* layer){ if (!layer) return false; if (fCurrentItem && fCurrentItem->layerPtr == layer){ return true; } for( ListData *item = fBottomItem; item != NULL; item = item->upperItem){ if (item->layerPtr == layer){ fCurrentItem = item; return true; } } return false; } //--------------------------------------------------------------------------- WinBorder* Workspace::SearchWinBorder(BPoint pt){ // TODO: implement correctly once you have clipping code working // For the moment, take windows from front to back and see in witch one 'pt' falls WinBorder *target = NULL; opLock.Lock(); for( WinBorder *wb = GoToBottomItem(); wb; wb = GoToUpperItem()){ // wb->PrintToStream(); if (!wb->IsHidden() && wb->HasPoint(pt)){ STRACE(("%s - SELECTED!\n", wb->GetName())); target = wb; break; } } opLock.Unlock(); return target; } //--------------------------------------------------------------------------- void Workspace::Invalidate(){ //TODO: *****!*!*!*!*!*!*!**!***REMOVE this! For Test purposes only! opLock.Lock(); if(fOwner->ActiveWorkspace() == this) fOwner->FullInvalidate(fOwner->Bounds()); opLock.Unlock(); //---------------- } //--------------------------------------------------------------------------- void Workspace::InsertItem(ListData* item, ListData* before){ // insert before one other item; if (before){ if (before->upperItem) before->upperItem->lowerItem = item; item->upperItem = before->upperItem; before->upperItem = item; item->lowerItem = before; // if we're inserting at top of the stack, change it accordingly. if(fTopItem == before) fTopItem = item; } // insert item at bottom. else{ item->upperItem = fBottomItem; if (fBottomItem) fBottomItem->lowerItem = item; fBottomItem = item; if (!fTopItem) fTopItem = item; } } //--------------------------------------------------------------------------- void Workspace::RemoveItem(ListData* item){ if (!item) return; if (fBottomItem == item){ fBottomItem = item->upperItem; } else{ item->lowerItem->upperItem = item->upperItem; } if (fTopItem == item){ fTopItem = item->lowerItem; } else{ item->upperItem->lowerItem = item->lowerItem; } // set all these to NULL to avoid confusion later. item->upperItem = NULL; item->lowerItem = NULL; if (fFocusItem == item){ fFocusItem->layerPtr->HighlightDecorator(false); fFocusItem = NULL; } if (fFrontItem == item) fFrontItem = NULL; if (fCurrentItem == item) fCurrentItem = NULL; } //--------------------------------------------------------------------------- ListData* Workspace::HasItem(ListData* item){ for (ListData *itemX = fBottomItem; itemX != NULL; itemX = itemX->upperItem){ if (item == itemX){ return itemX; } } return NULL; } //--------------------------------------------------------------------------- ListData* Workspace::HasItem(WinBorder* layer){ for (ListData *item = fBottomItem; item != NULL; item = item->upperItem){ if (item->layerPtr == layer){ return item; } } return NULL; } /* This, also is a "smart" method. Firstly this funtionality was in * SearchAndSetNewFront, but I've split it for reasons of clarity. Anyway, * what is to be noticed is that those 2 methods work hand-in-hand. * What FindPlace() litelaly does, is *place* the 'pref' item into its * >right< place! If 'pref->layerPtr'(the WinBorder in ListData structure) * does not meet some conditions a search for another, valid, WinBorder is * being made. * * NOTE: Do NOT be confussed! This method places the preferred window *ONLY*! * Other windows that need to be placed before it, *WILL* be placed by almost * all code found in SearchAndSetNewFront */ //--------------------------------------------------------------------------- ListData* Workspace::FindPlace(ListData* pref){ // if we received a NULL value, we stil have to give 'front' state to some window... if (!pref) pref = HasItem(fBottomItem); else pref = HasItem(pref); ListData *item = NULL; // search a window that is not hidden and does *not* have B_AVOID_FRONT flag. item = pref; while(pref && item->lowerItem != pref && (pref->upperItem || pref->lowerItem)){ if ( !(item->layerPtr->Window()->Flags() & B_AVOID_FRONT) && !(item->layerPtr->IsHidden()) ) break; STRACE(("item: %s - pref: %s\n", item->layerPtr->GetName(), pref->layerPtr->GetName())); if (item == fTopItem) item = fBottomItem; else item = item->upperItem; } // if true, it means we have windows with B_AVOID_FRONT flag *only*, // or they are all hidden. Pick one, if you can. if (pref && item->lowerItem == pref){ for (item = fBottomItem; item; item = item->upperItem){ if ( !(item->layerPtr->IsHidden()) ) break; } } // if the search took place or not... pref = item; // we have exhausted all possibilities to make pref a vaid pointer... so... exit now. if (!pref) return NULL; // temporally remove 'pref' to reinsert it later in the *right* place RemoveItem(pref); // we start searching its place ListData *cursor = fBottomItem; int32 feel = pref->layerPtr->Window()->Feel(); switch(feel){ case B_NORMAL_WINDOW_FEEL:{ while(cursor && cursor->layerPtr->fLevel > B_MODAL_APP_FEEL){ cursor = cursor->upperItem; } InsertItem(pref, cursor? cursor->lowerItem: NULL); break; } case B_SYSTEM_LAST:{ while(cursor && cursor->layerPtr->fLevel > pref->layerPtr->fLevel){ cursor = cursor->upperItem; } InsertItem(pref, cursor? cursor->lowerItem: fTopItem); break; } case B_SYSTEM_FIRST: case B_FLOATING_ALL_WINDOW_FEEL: case B_MODAL_ALL_WINDOW_FEEL: case B_MODAL_APP_WINDOW_FEEL:{ while(cursor && cursor->layerPtr->fLevel > pref->layerPtr->fLevel){ cursor = cursor->upperItem; } InsertItem(pref, cursor? cursor->lowerItem: NULL); break; } // place in front of: its main window, other subset windows and in front // of other application's floating windows. // NOTE that this happens only if its main window is the front most one. case B_FLOATING_SUBSET_WINDOW_FEEL:{ for(cursor = fBottomItem; cursor; cursor = cursor->upperItem){ if (cursor->layerPtr->fLevel <= pref->layerPtr->fLevel && (cursor->layerPtr == pref->layerPtr->MainWinBorder() || cursor->layerPtr->MainWinBorder() == pref->layerPtr->MainWinBorder()) ) { break; } else if(pref->layerPtr->fLevel == B_FLOATING_SUBSET_FEEL && cursor->layerPtr->fLevel == B_FLOATING_APP_FEEL) { break; } } if (cursor) InsertItem(pref, cursor? cursor->lowerItem: NULL); break; } // place this SUBSET_MODAL behind APP_MODAL ones if they belong // to the same application OR, in front of all MODAL/NORMAL if // it belongs to another application case B_MODAL_SUBSET_WINDOW_FEEL:{ for(cursor = fBottomItem; cursor; cursor = cursor->upperItem){ if (cursor->layerPtr->fLevel <= pref->layerPtr->fLevel) break; else if(pref->layerPtr->fLevel == B_MODAL_SUBSET_FEEL && cursor->layerPtr->fLevel == B_MODAL_APP_FEEL && pref->layerPtr->Window()->ClientTeamID() != cursor->layerPtr->Window()->ClientTeamID()) break; } if (cursor) InsertItem(pref, cursor? cursor->lowerItem: NULL); break; } // place in front of: its main window, other floating windows // NOTE that this happens only if its main window is the front most one. case B_FLOATING_APP_WINDOW_FEEL:{ for(cursor = fBottomItem; cursor; cursor = cursor->upperItem){ if (cursor->layerPtr->fLevel <= pref->layerPtr->fLevel && pref->layerPtr->Window()->ClientTeamID() == cursor->layerPtr->Window()->ClientTeamID()) { break; } } if (cursor) InsertItem(pref, cursor? cursor->lowerItem: NULL); break; } } return pref; } /* This method is the key to correct window arangement!!! * With the help of FindPlace it correctly aranges windows. FindPlace, only * inserted the window in the correct place, this method also brings all * other windows that are supposed to be in front of her - I'm talking here * about floating and modal windows. * It also clerverly selects the new FINAL front window. */ //--------------------------------------------------------------------------- void Workspace::SearchAndSetNewFront(WinBorder* preferred){ STRACE(("*WS(%ld)::SASNF(%s)\n", ID(), preferred? preferred->GetName(): "NULL")); opLock.Lock(); // the new front must not be the same as the previous one. if (fFrontItem && fFrontItem->layerPtr == preferred && !(preferred->IsHidden())){ STRACE(("-WS(%ld)::SASNF(%s) - opperation not needed! Workspace data:", ID(), preferred? preferred->GetName(): "NULL")); STRACESTREAM(); STRACE(("#WS(%ld)::SASNF(%s) ENDED 1\n", ID(), preferred? preferred->GetName(): "NULL")); opLock.Unlock(); return; } // properly place this 'preferred' WinBorder. ListData *lastInserted; lastInserted = FindPlace(HasItem(preferred)); preferred = lastInserted? lastInserted->layerPtr: NULL; STRACE(("-WS(%ld)::SASNF(%s) - after FindPlace...", ID(), preferred? preferred->GetName(): "NULL")); STRACESTREAM(); // if the new front layer is the same... there is no point continuing if(fFrontItem == lastInserted){ STRACE(("-WS(%ld)::SASNF(%s) - new front layer is the same as the old one. Stop!", ID(), preferred? preferred->GetName(): "NULL")); STRACESTREAM(); STRACE(("#WS(%ld)::SASNF(%s) ENDED 2\n", ID(), preferred? preferred->GetName(): "NULL")); opLock.Unlock(); return; } if(!lastInserted){ STRACE(("PAAAAANIC: Workspace::SASNF(): 'lastInserted' IS NULL\n")); } else{ STRACE(("\n&&&&Processing for: %s\n", lastInserted->layerPtr->GetName())); } if (lastInserted && !(lastInserted->layerPtr->IsHidden())){ int32 prefFeel = preferred->Window()->Feel(); if (prefFeel == B_NORMAL_WINDOW_FEEL){ STRACE((" NORMAL Window '%s' -", preferred? preferred->GetName(): "NULL")); if (fFrontItem){ // if they are in the same team... if (preferred->Window()->ClientTeamID() == fFrontItem->layerPtr->Window()->ClientTeamID()){ STRACE((" SAME TeamID\n")); // collect subset windows that are common to application's windows... // NOTE: A subset window *can* be added to more than just one window. FMWList commonFMW; FMWList appFMW; FMWList finalFMWList; int32 count, i; // collect floating and modal windows spread across the workspace only if // they are in our window's subset // * also remove them, for repositioning, later. ListData *listItem = fTopItem; while(listItem){ int32 feel = listItem->layerPtr->Window()->Feel(); if (feel == B_FLOATING_SUBSET_WINDOW_FEEL || feel == B_MODAL_SUBSET_WINDOW_FEEL){ if(preferred->Window()->fWinFMWList.HasItem(listItem->layerPtr)){ commonFMW.AddItem(listItem->layerPtr); // *carefully* remove the item from the list ListData *item = listItem; listItem = listItem->lowerItem; RemoveItem(item); } // also remove floating windows. Those, SURELY belong to // 'fFrontItem' - this being the *old* front window. else if (feel == B_FLOATING_SUBSET_WINDOW_FEEL){ // *carefully* remove the item from the list ListData *item = listItem; listItem = listItem->lowerItem; RemoveItem(item); } else{ listItem = listItem->lowerItem; } } // ALSO collect application's floating and modal windows, // for reinsertion, later. else if (feel == B_FLOATING_APP_WINDOW_FEEL || feel == B_MODAL_APP_WINDOW_FEEL){ if(listItem->layerPtr->Window()->ClientTeamID() == preferred->Window()->ClientTeamID()) { appFMW.AddItem(listItem->layerPtr); // *carefully* remove the item from the list ListData *item = listItem; listItem = listItem->lowerItem; RemoveItem(item); } else listItem = listItem->lowerItem; } else listItem = listItem->lowerItem; } // put in the final list, items that are not found in the common list count = preferred->Window()->fWinFMWList.CountItems(); for (i=0; iWindow()->fWinFMWList.ItemAt(i); if (commonFMW.HasItem(item)){ } else finalFMWList.AddItem(item); } // colapse the 2 lists finalFMWList.AddFMWList(&commonFMW); finalFMWList.AddFMWList(&appFMW); // insert windows found in 'finalFMWList' in Workspace's list. // IF *one* modal is found, do not add floating ones! // see if the last WinBorder in the list is a modal window. // OR if the last one in Workspace's list is a modal window... WinBorder *wb = (WinBorder*)finalFMWList.LastItem(); bool lastIsModal = false; if ( (wb && (wb->Window()->Feel() == B_MODAL_SUBSET_WINDOW_FEEL || wb->Window()->Feel() == B_MODAL_APP_WINDOW_FEEL)) || (fBottomItem && (fBottomItem->layerPtr->Window()->Feel() == B_MODAL_ALL_WINDOW_FEEL || fBottomItem->layerPtr->Window()->Feel() == B_SYSTEM_FIRST)) ) { lastIsModal = true; } // this variable will help us in deciding the new front WinBorder. WinBorder *finalPreferred = preferred; // now insert items found in finalFMWList into Workspace count = finalFMWList.CountItems(); for(i=0; iWindow()->Feel() == B_FLOATING_SUBSET_WINDOW_FEEL || wb->Window()->Feel() == B_FLOATING_APP_WINDOW_FEEL) { // set its new MainWinBorder if(wb->Window()->Feel() == B_FLOATING_SUBSET_WINDOW_FEEL) wb->SetMainWinBorder(preferred); // don't add if the last WinBorder is a modal one. if (lastIsModal) continue; } else{ if(wb->Window()->Feel() == B_MODAL_SUBSET_WINDOW_FEEL) wb->SetMainWinBorder(preferred); // if this modal window is not hidden, give it the front status. if (!(wb->IsHidden())) finalPreferred = wb; } // insert item just after the last inserted one. ListData *newItem = new ListData(); newItem->layerPtr = wb; newItem->lowerItem = lastInserted->lowerItem; newItem->upperItem = lastInserted; if (lastInserted->lowerItem) lastInserted->lowerItem->upperItem = newItem; lastInserted->lowerItem = newItem; if(lastInserted == fBottomItem) fBottomItem = newItem; lastInserted = newItem; } preferred = finalPreferred; } else{ STRACE((" DIFERRENT TeamID\n")); // remove front window's floating(_SUBSET_/_APP_) windows, if any. ListData *listItem = fFrontItem->lowerItem; while(listItem && (listItem->layerPtr->fLevel == B_FLOATING_SUBSET_FEEL || listItem->layerPtr->fLevel == B_FLOATING_APP_FEEL)) { // *carefully* remove the item from the list ListData *item = listItem; listItem = listItem->lowerItem; RemoveItem(item); } // now, jump to et1: to make a 'clean' selecting of // windows to be inserted fFrontItem = NULL; goto et1; } // END: else - if diferrent ClientTeamID()s. } // if (fFrontItem) && NORMAL_WINDOW else{ STRACE((" NO previous FRONT Item\n")); et1: FMWList finalFMWList; int32 count, i; ListData *listItem = fTopItem; // remove window's subset and application's *modal* windows spread // across Workspace's list, to be insert later, in the corect order. while(listItem){ int32 feel = listItem->layerPtr->Window()->Feel(); if ((feel == B_MODAL_SUBSET_WINDOW_FEEL && preferred->Window()->fWinFMWList.HasItem(listItem->layerPtr)) || (feel == B_MODAL_APP_WINDOW_FEEL && preferred->Window()->ClientTeamID() == listItem->layerPtr->Window()->ClientTeamID() )) { // *carefully* remove the item from the list ListData *item = listItem; listItem = listItem->lowerItem; RemoveItem(item); } else listItem = listItem->lowerItem; } // add to the final list window's subset windows. (..._SUBSET_...) finalFMWList.AddFMWList(&(preferred->Window()->fWinFMWList)); // also add application's ones. (..._APP_...) finalFMWList.AddFMWList(&(preferred->Window()->App()->fAppFMWList)); // insert windows found in 'finalFMWList' in Workspace's list. // IF *one* modal is found, do not add floating ones! // see if the last WinBorder in the list is a modal window. WinBorder *wb = (WinBorder*)finalFMWList.LastItem(); bool lastIsModal = false; if ( (wb && (wb->Window()->Feel() == B_MODAL_SUBSET_WINDOW_FEEL || wb->Window()->Feel() == B_MODAL_APP_WINDOW_FEEL)) || (fBottomItem && (fBottomItem->layerPtr->Window()->Feel() == B_MODAL_ALL_WINDOW_FEEL || fBottomItem->layerPtr->Window()->Feel() == B_SYSTEM_FIRST)) ) { lastIsModal = true; } // this variable will help us in deciding the new front WinBorder. WinBorder *finalPreferred = preferred; // now insert items found in finalFMWList into Workspace count = finalFMWList.CountItems(); for(i=0; iWindow()->Feel() == B_FLOATING_SUBSET_WINDOW_FEEL || wb->Window()->Feel() == B_FLOATING_APP_WINDOW_FEEL) { // set its new MainWinBorder if(wb->Window()->Feel() == B_FLOATING_SUBSET_WINDOW_FEEL) wb->SetMainWinBorder(preferred); // don't add if the last WinBorder is a modal one. if (lastIsModal) continue; } else{ if(wb->Window()->Feel() == B_MODAL_SUBSET_WINDOW_FEEL) wb->SetMainWinBorder(preferred); // if this modal window is not hidden, give it the front status. if (!(wb->IsHidden())) finalPreferred = wb; } // insert item just after the last inserted one. ListData *newItem = new ListData(); newItem->layerPtr = wb; newItem->lowerItem = lastInserted->lowerItem; newItem->upperItem = lastInserted; if (lastInserted->lowerItem) lastInserted->lowerItem->upperItem = newItem; lastInserted->lowerItem = newItem; if(lastInserted == fBottomItem) fBottomItem = newItem; lastInserted = newItem; } preferred = finalPreferred; } // else - if(fFrontItem); } // END: if _NORMAL_ window. else if( prefFeel == B_FLOATING_SUBSET_WINDOW_FEEL || prefFeel == B_FLOATING_APP_WINDOW_FEEL || prefFeel == B_FLOATING_ALL_WINDOW_FEEL) { STRACE((" FLOATING Window '%s'\n", preferred? preferred->GetName(): "NULL")); // Do nothing! FindPlace() was called and it has corectly placed our window // We don't have to do noting here. } else if( prefFeel == B_MODAL_SUBSET_WINDOW_FEEL || prefFeel == B_MODAL_APP_WINDOW_FEEL) { STRACE((" MODAL_APP/SUBSET Window '%s'\n", preferred? preferred->GetName(): "NULL")); FMWList finalMWList; int32 count = 0, i; int32 prefIndex = -1; if (fFrontItem && fFrontItem->layerPtr->fLevel == B_NORMAL_FEEL) { // remove front window's floating(_SUBSET_/_APP_) windows, if any. ListData *listItem = fFrontItem->lowerItem; while(listItem && (listItem->layerPtr->fLevel == B_FLOATING_SUBSET_FEEL || listItem->layerPtr->fLevel == B_FLOATING_APP_FEEL)) { // *carefully* remove the item from the list ListData *item = listItem; listItem = listItem->lowerItem; RemoveItem(item); } } // If this is a MODAL_SUBSET window, search it's place in its MainWinBorder // window subset, and then take all modals after it; then continue talking // modal windoes *only*, from application's set. // If it is a MODAL_APP only search an take windows in/from application's set if (prefFeel == B_MODAL_SUBSET_WINDOW_FEEL){ prefIndex = preferred->MainWinBorder()->Window()->fWinFMWList.IndexOf(preferred); count = preferred->MainWinBorder()->Window()->fWinFMWList.CountItems(); if (prefIndex >= 0){ WinBorder *wb = NULL; // add modal windows from its main window subset - windows that are // positioned after 'preferred'. FMWList *listPtr = &(preferred->MainWinBorder()->Window()->fWinFMWList); for(i = prefIndex+1; i < count; i++){ // they *all* are modal windows. wb = (WinBorder*)listPtr->ItemAt(i); wb->SetMainWinBorder(preferred); finalMWList.AddItem(wb); // remove those windows(if in there), for correct re-insertion later. ListData *ld = NULL; if((ld = HasItem(wb))) RemoveItem(ld); } // add modal windows that are found in application's subset count = preferred->Window()->App()->fAppFMWList.CountItems(); listPtr = &(preferred->Window()->App()->fAppFMWList); for(i = 0; i < count; i++){ wb = (WinBorder*)listPtr->ItemAt(i); if (wb->Window()->Feel() == B_MODAL_APP_WINDOW_FEEL){ finalMWList.AddItem(wb); // remove those windows(if in there), for correct re-insertion later. ListData *ld = NULL; if((ld = HasItem(wb))) RemoveItem(ld); } } } } else if(prefFeel == B_MODAL_APP_WINDOW_FEEL){ prefIndex = preferred->Window()->App()->fAppFMWList.IndexOf(preferred); count = preferred->Window()->App()->fAppFMWList.CountItems(); if (prefIndex >= 0){ WinBorder *wb = NULL; // add modal windows from application's subset - windows that are // positioned after 'preferred'. FMWList *listPtr = &(preferred->Window()->App()->fAppFMWList); for(i = prefIndex+1; i < count; i++){ wb = (WinBorder*)listPtr->ItemAt(i); // they *all* are modal windows. finalMWList.AddItem(wb); // remove those windows(if in there), for correct re-insertion later. ListData *ld = NULL; if((ld = HasItem(wb))) RemoveItem(ld); } } } // insert windows found in 'finalMWList' in Workspace's list, after "preferred" count = finalMWList.CountItems(); // this varable will help us designate the future front. WinBorder *finalPreferred = preferred; for(i=0; iIsHidden())) finalPreferred = wb; // insert item just after the last inserted one. ListData *newItem = new ListData(); newItem->layerPtr = wb; newItem->lowerItem = lastInserted->lowerItem; newItem->upperItem = lastInserted; if (lastInserted->lowerItem) lastInserted->lowerItem->upperItem = newItem; lastInserted->lowerItem = newItem; if(lastInserted == fBottomItem){ fBottomItem = newItem; } lastInserted = newItem; } preferred = finalPreferred; } else if(prefFeel == B_MODAL_ALL_WINDOW_FEEL || prefFeel == B_SYSTEM_FIRST) { STRACE((" MODAL ALL/SYSTEM FIRST Window '%s'\n", preferred? preferred->GetName(): "NULL")); // remove all application's floating windows. if (fFrontItem && fFrontItem->layerPtr->fLevel == B_NORMAL_FEEL) { ListData *listItem = fFrontItem->lowerItem; while(listItem && (listItem->layerPtr->fLevel == B_FLOATING_SUBSET_FEEL || listItem->layerPtr->fLevel == B_FLOATING_APP_FEEL)) { // *carefully* remove the item from the list ListData *item = listItem; listItem = listItem->lowerItem; RemoveItem(item); } } } else if(prefFeel == B_SYSTEM_LAST){ ; // Do Nothing. } else{ // We ***should NOT*** reach this point! STRACE(("SERVER: PANIC: \"%s\": What kind of window is this???\n", preferred? preferred->GetName(): "NULL")); } } else STRACE((" The window IS Hidden\n")); ListData *exFrontItem = fFrontItem; ListData *newFrontItem = NULL; if(preferred && fBottomItem){ int32 feel = fBottomItem->layerPtr->Window()->Feel(); // if preferred is one of these *don't* give front state to it! if(preferred->fLevel == B_FLOATING_SUBSET_FEEL || preferred->fLevel == B_FLOATING_APP_FEEL || preferred->fLevel == B_FLOATING_ALL_FEEL) { newFrontItem = exFrontItem; } // if the last in workspace's list is one of these, GIVE focus to it! else if((feel == B_SYSTEM_FIRST || feel == B_MODAL_ALL_WINDOW_FEEL) && !(fBottomItem->layerPtr->IsHidden()) ) { newFrontItem = fBottomItem; } // the SYSTEM_LAST will get the front status, only if it's the only // WinBorder in this workspace. else if (preferred->fLevel == B_SYSTEM_LAST && !(preferred->IsHidden()) ) { if(fBottomItem->layerPtr == preferred) newFrontItem = HasItem(preferred); } // this is in case of MODAL[APP/SUBSET] and NORMAL windows else if ( !(preferred->IsHidden()) ){ newFrontItem = HasItem(preferred); } else{ newFrontItem = NULL; } } else{ newFrontItem = NULL; } if (fFrontItem != newFrontItem){ fFrontItem = newFrontItem; // TODO: call a method like... WinBorder::MakeFront(true); } opLock.Unlock(); STRACE(("#WS(%ld)::SASNF(%s) ENDED! Workspace data...", ID(), preferred? preferred->GetName(): "NULL")); STRACESTREAM(); } /* This method performs a simple opperation. It searched the new focus window * by walking from front to back, cheking for some things, until all variables * correspond. */ //--------------------------------------------------------------------------- void Workspace::SearchAndSetNewFocus(WinBorder* preferred){ STRACE(("*WS(%ld)::SASNFocus(%s)\n", ID(), preferred? preferred->GetName(): "NULL")); opLock.Lock(); if(!preferred) preferred = fBottomItem? fBottomItem->layerPtr : NULL; bool selectOthers = false; ListData *item = NULL; for(item = fBottomItem; item != NULL; item = item->upperItem){ // if this WinBorder doesn't want to have focus... get to the next one if (item->layerPtr->Window()->Flags() & B_AVOID_FOCUS) continue; if (preferred && item->layerPtr == preferred){ // our preffered one is hidden so... select another one if (preferred && preferred->IsHidden()){ selectOthers = true; continue; } else{ break; } } if (item->layerPtr->fLevel == B_SYSTEM_FIRST || item->layerPtr->fLevel == B_MODAL_ALL_FEEL) { break; } if (item->layerPtr->fLevel == B_MODAL_APP_FEEL && (preferred && preferred->Window()->ClientTeamID() == item->layerPtr->Window()->ClientTeamID())) { break; } if (item->layerPtr->fLevel == B_MODAL_SUBSET_FEEL && (preferred && item->layerPtr->MainWinBorder() == preferred)) { break; } // select one window, other than a system_last one! if (selectOthers && item->layerPtr->fLevel != B_SYSTEM_LAST){ break; } } // there are no windows below us to select. take the one from above us. if(selectOthers && !item){ // there HAS to be valid item = HasItem(preferred); if (item) item= item->lowerItem; } // there are NO windows in front of us, and no regular window below us // maybe there is a system_last window... if(!item){ item = fTopItem; } if (item != fFocusItem){ // TODO: item->ColorTabInGrey & send message to client // TODO: item->ColorTabInYellow & send message to client // TODO: Rebuild & Redraw. fFocusItem = item; } opLock.Unlock(); } //--------------------------------------------------------------------------- void Workspace::BringToFrontANormalWindow(WinBorder* layer){ switch (layer->Window()->Feel()){ case B_FLOATING_SUBSET_WINDOW_FEEL: case B_MODAL_SUBSET_WINDOW_FEEL:{ SearchAndSetNewFront(layer->MainWinBorder()); break; } case B_FLOATING_APP_WINDOW_FEEL: case B_MODAL_APP_WINDOW_FEEL:{ opLock.Lock(); ListData *item = fBottomItem; team_id tid = layer->Window()->ClientTeamID(); while(item){ if(item->layerPtr->Window()->ClientTeamID() == tid && item->layerPtr->Window()->Feel() == B_NORMAL_WINDOW_FEEL) { break; } item = item->upperItem; } if(item){ SearchAndSetNewFront(item->layerPtr); } opLock.Unlock(); break; } default:{ // in case of MODAL/FLOATING_ALL or _NORMAL_ or SYSTEM_FIRST/LAST do nothing! } } } /* The method moves a window to the back of its subset. */ //--------------------------------------------------------------------------- void Workspace::MoveToBack(WinBorder* newLast){ STRACE(("\n!MoveToBack(%s) -", newLast? newLast->GetName(): "NULL")); // does the list have this element? ListData *item = HasItem(newLast); if(item){ ListData *listItem = NULL; switch(item->layerPtr->fLevel){ case B_FLOATING_ALL_FEEL:{ STRACE((" B_FLOATING_ALL_FEEL window\n")); // search the place where we should insert it later listItem = item->upperItem; while(listItem && (listItem->layerPtr->fLevel == item->layerPtr->fLevel)) listItem = listItem->upperItem; break; } // those 2 flags form a 'virtual' set, so even if FLOATING_APP // has a greater priority than FLOATING_SUBSET, it is still moved // behind FLOATING_SUBSET. case B_FLOATING_SUBSET_FEEL: case B_FLOATING_APP_FEEL:{ STRACE((" B_FLOATING_SUBSET_FEEL/B_FLOATING_APP_FEEL window\n")); // search the place where we should insert it later listItem = item->upperItem; while(listItem && (listItem->layerPtr->fLevel == B_FLOATING_SUBSET_FEEL || listItem->layerPtr->fLevel == B_FLOATING_APP_FEEL)) listItem = listItem->upperItem; break; } // the same like with floating window. Those 2 and NORMAL form a'virtual' set. case B_MODAL_SUBSET_FEEL: case B_MODAL_APP_FEEL:{ STRACE((" B_MODAL_SUBSET_FEEL/B_MODAL_APP_FEEL window\n")); // search the place where we should insert it later listItem = item->upperItem; while(listItem && (listItem->layerPtr->fLevel == B_MODAL_SUBSET_FEEL || listItem->layerPtr->fLevel == B_MODAL_APP_FEEL || listItem->layerPtr->fLevel == B_NORMAL_FEEL)) listItem = listItem->upperItem; break; } // here is the simplest, we use the priority levels. case B_NORMAL_FEEL:{ STRACE((" B_NORMAL_FEEL window\n")); listItem = item->upperItem; while(listItem && (listItem->layerPtr->fLevel >= item->layerPtr->fLevel)){ listItem = listItem->upperItem; } break; } } if ((listItem && item->lowerItem == listItem->lowerItem) || (listItem == NULL && item == fTopItem)) { // do nothing! The window will be in the same position it is now. } else{ // if it's a normal window, first remove any floating windows, // it or its application has if(newLast->fLevel == B_NORMAL_FEEL && fFrontItem->layerPtr == newLast){ ListData *iter = item->lowerItem; while(iter && (iter->layerPtr->fLevel == B_FLOATING_SUBSET_FEEL || iter->layerPtr->fLevel == B_FLOATING_APP_FEEL)) { // *carefully* remove the item from the list ListData *itemX = iter; iter = iter->lowerItem; RemoveItem(itemX); } } WinBorder *nextPreferred = item->upperItem? item->upperItem->layerPtr: NULL; bool wasFront = fFrontItem->layerPtr == newLast; bool wasFocus = fFocusItem->layerPtr == newLast; // remove item RemoveItem(item); // insert in the new, right' position. InsertItem(item, listItem? listItem->lowerItem: fTopItem); if (wasFront) SearchAndSetNewFront(nextPreferred); if (wasFocus) SearchAndSetNewFocus(nextPreferred); } STRACESTREAM(); } else STRACE((" this operation was not needed\n")); } //--------------------------------------------------------------------------- void Workspace::SetLocalSpace(const uint32 colorspace){ fSpace = colorspace; } //--------------------------------------------------------------------------- uint32 Workspace::LocalSpace() const{ return fSpace; } //--------------------------------------------------------------------------- void Workspace::SetBGColor(const RGBColor &c){ fBGColor = c; } //--------------------------------------------------------------------------- RGBColor Workspace::BGColor(void) const{ return fBGColor; } //--------------------------------------------------------------------------- // Debug methods!!! //--------------------------------------------------------------------------- void Workspace::PrintToStream() const{ printf("\nWorkspace %ld hierarchy shown from back to front:\n", fID); for (ListData *item = fTopItem; item != NULL; item = item->lowerItem){ WinBorder *wb = (WinBorder*)item->layerPtr; printf("\tName: %s\t%s", wb->GetName(), wb->IsHidden()?"Hidden\t": "NOT Hidden"); if (wb->Window()->Feel() == B_FLOATING_SUBSET_WINDOW_FEEL) printf("\t%s\n", "B_FLOATING_SUBSET_WINDOW_FEEL"); if (wb->Window()->Feel() == B_FLOATING_APP_WINDOW_FEEL) printf("\t%s\n", "B_FLOATING_APP_WINDOW_FEEL"); if (wb->Window()->Feel() == B_FLOATING_ALL_WINDOW_FEEL) printf("\t%s\n", "B_FLOATING_ALL_WINDOW_FEEL"); if (wb->Window()->Feel() == B_MODAL_SUBSET_WINDOW_FEEL) printf("\t%s\n", "B_MODAL_SUBSET_WINDOW_FEEL"); if (wb->Window()->Feel() == B_MODAL_APP_WINDOW_FEEL) printf("\t%s\n", "B_MODAL_APP_WINDOW_FEEL"); if (wb->Window()->Feel() == B_MODAL_ALL_WINDOW_FEEL) printf("\t%s\n", "B_MODAL_ALL_WINDOW_FEEL"); if (wb->Window()->Feel() == B_NORMAL_WINDOW_FEEL) printf("\t%s\n", "B_NORMAL_WINDOW_FEEL"); if (wb->Window()->Feel() == B_SYSTEM_LAST) printf("\t%s\n", "B_SYSTEM_LAST"); if (wb->Window()->Feel() == B_SYSTEM_FIRST) printf("\t%s\n", "B_SYSTEM_FIRST"); } printf("Focus Layer:\t%s\n", fFocusItem? fFocusItem->layerPtr->GetName(): "NULL"); printf("Front Layer:\t%s\n", fFrontItem? fFrontItem->layerPtr->GetName(): "NULL"); // printf("Current Layer:\t%s\n", fCurrentItem? fCurrentItem->layerPtr->GetName(): "NULL"); // printf("Top Layer:\t%s\n", fTopItem? fTopItem->layerPtr->GetName(): "NULL"); // printf("Bottom Layer:\t%s\n", fBottomItem? fBottomItem->layerPtr->GetName(): "NULL"); } //--------------------------------------------------------------------------- void Workspace::PrintItem(ListData *item) const{ printf("ListData members:\n"); if (item){ printf("WinBorder:\t%s\n", item->layerPtr? item->layerPtr->GetName(): "NULL"); printf("UpperItem:\t%s\n", item->upperItem? item->upperItem->layerPtr->GetName(): "NULL"); printf("LowerItem:\t%s\n", item->lowerItem? item->lowerItem->layerPtr->GetName(): "NULL"); } else{ printf("NULL item!!!\n"); } }