#include "XMLToolBarFrame.h" #include "WithXMLMenu.h" NAMESPACE_UPP // recalculates relative toolbar's positions // needed after adding or removing a toolbar void XMLToolBarFrame::Reposition(void) { // clears position mapper posMapper.Clear(); // setup a dummy frame size, just in case frame is empty frameSize = 3; // if no toolbars inside, just do nothing else if(!toolBars.GetCount()) return; // fixup 'vertical' positions, i.e. rows on which toolbars belong ArrayvPos; for(int iPos = 0; iPos < relativePositions.GetCount(); iPos++) { Size const &sz = relativePositions[iPos]; vPos.Add(sz.cy); } Sort(vPos); VectorMapvMap; int v = 0; for(int iPos = 0; iPos < vPos.GetCount(); iPos++) { if(vMap.Find(vPos[iPos]) < 0) { vMap.Add(vPos[iPos], v); v ++; } } for(int iPos = 0; iPos < relativePositions.GetCount(); iPos++) { Size &sz = relativePositions[iPos]; sz.cy = vMap.Get(sz.cy); } // now, scans each row, fixing up positions inside it and // building true toolbars positions int curVPos = 0; for(int iRow = 0; iRow < vMap.GetCount(); iRow++) { Vectorps, idx; int rowHeight = 0; for(int iPos = 0; iPos < relativePositions.GetCount(); iPos++) { Size &sz = relativePositions[iPos]; if(sz.cy == iRow) { ps.Add(sz.cx); idx.Add(iPos); } } IndexSort(ps, idx); int minNextPos = 0; for(int i = 0; i < ps.GetCount(); i++) { if(ps[i] < minNextPos) ps[i] = minNextPos; XMLToolBarCtrl &tb = *toolBars[idx[i]]; Size sz; switch(toolBarState) { case TOOLBAR_LEFT : case TOOLBAR_RIGHT : sz = tb.GetVertSize(); rowHeight = max(rowHeight, sz.cx); minNextPos = ps[i] + sz.cy + 2; // 2 pixels gap between columns break; case TOOLBAR_TOP : case TOOLBAR_BOTTOM : sz = tb.GetHorzSize(); rowHeight = max(rowHeight, sz.cy); minNextPos = ps[i] + sz.cx + 2; // 2 pixels gap between columns break; default: NEVER(); } } VectorMap &rowMap = posMapper.Add(curVPos); for(int i = 0; i < ps.GetCount(); i++) rowMap.Add(ps[i], idx[i]); curVPos += rowHeight; } // store total frame size frameSize = curVPos; // if reversed direction, fixup cols if(toolBarState == TOOLBAR_RIGHT || toolBarState == TOOLBAR_BOTTOM) { int lastPos = posMapper.GetKey(posMapper.GetCount()-1); for(int iRow = 0; iRow < posMapper.GetCount(); iRow++) posMapper.SetKey(iRow, lastPos - posMapper.GetKey(iRow)); } } void XMLToolBarFrame::FrameLayout(Rect& r) { // stores frame rectangle -- it's needed to layout toolbars frameRect = r; Reposition(); switch(toolBarState) { case TOOLBAR_LEFT: r.left += frameSize + 4; frameRect.right = r.left; frameP11 = Point(frameRect.left, frameRect.top); frameP12 = Point(frameRect.left, frameRect.bottom); frameP21 = Point(frameRect.right-1, frameRect.top); frameP22 = Point(frameRect.right-1, frameRect.bottom); frameRect.left += 2; frameRect.right -= 2; break; case TOOLBAR_RIGHT: r.right -= frameSize + 4; frameRect.left = r.right; frameP11 = Point(frameRect.left, frameRect.top); frameP12 = Point(frameRect.left, frameRect.bottom); frameP21 = Point(frameRect.right-1, frameRect.top); frameP22 = Point(frameRect.right-1, frameRect.bottom); frameRect.left += 2; frameRect.right -= 2; break; case TOOLBAR_TOP: r.top += frameSize + 4; frameRect.bottom = r.top; frameP11 = Point(frameRect.left, frameRect.top); frameP12 = Point(frameRect.right, frameRect.top); frameP21 = Point(frameRect.left, frameRect.bottom-1); frameP22 = Point(frameRect.right, frameRect.bottom-1); frameRect.top += 2; frameRect.bottom -= 2; break; case TOOLBAR_BOTTOM: r.bottom -= frameSize + 4; frameRect.top = r.bottom; frameP11 = Point(frameRect.left, frameRect.top); frameP12 = Point(frameRect.right, frameRect.top); frameP21 = Point(frameRect.left, frameRect.bottom-1); frameP22 = Point(frameRect.right, frameRect.bottom-1); frameRect.top += 2; frameRect.bottom -= 2; break; default: NEVER(); } // fixup control inside frame toolBarContainer.SetFrameRect( frameRect.left, frameRect.top, frameRect.GetSize().cx, frameRect.GetSize().cy ); // layouts toolbars inside frame Layout(); } void XMLToolBarFrame::FrameAddSize(Size& sz) { Reposition(); switch(toolBarState) { case TOOLBAR_LEFT: case TOOLBAR_RIGHT: sz.cx += frameSize + 4; break; case TOOLBAR_TOP: case TOOLBAR_BOTTOM: sz.cy += frameSize + 4; break; default: NEVER(); break; } } // frame painting void XMLToolBarFrame::FramePaint(Draw& w, const Rect& r) { // w.DrawRect(r, Red()); w.DrawRect(r, SColorLtFace()); // w.DrawLine(frameP11, frameP12, 0, SColorLight()); // w.DrawLine(frameP21, frameP22, 0, SColorLtFace()); // if predocking, paint the docking toolBar placeholder /* if(preDocking) w.DrawRect(preDockRect, Yellow()); */ } // lays toolbars inside frame void XMLToolBarFrame::Layout(void) { // don't layout if still no parent if(!parent) return; for(int iRow = 0; iRow < posMapper.GetCount(); iRow++) { int rowPos = posMapper.GetKey(iRow); VectorMap &rowMapper = posMapper[iRow]; for(int iCol = 0; iCol < rowMapper.GetCount(); iCol++) { int colPos = rowMapper.GetKey(iCol); int idx = rowMapper[iCol]; XMLToolBarCtrl &tb = *toolBars[idx]; Ctrl::LogPos pos; Size sz; int x1, y1, x2, y2; switch(toolBarState) { case TOOLBAR_LEFT : case TOOLBAR_RIGHT : sz = tb.GetVertSize(); x1 = rowPos; y1 = colPos; break; case TOOLBAR_TOP : case TOOLBAR_BOTTOM : sz = tb.GetHorzSize(); x1 = colPos; y1 = rowPos; break; default : NEVER(); break; } x2 = x1 + sz.cx; y2 = y1 + sz.cy; // don't add last toolbar as a child if predocking if(!preDocking || &tb != toolBars.Top()) { pos = Ctrl::LogPos( Ctrl::PosLeft(x1, x2 - x1), Ctrl::PosTop(y1, y2 - y1) ); tb.SetPos(pos); } if(preDocking && &tb == toolBars.Top()) preDockRect = Rect(x1, y1, x2, y2); } } } // frame insertion/removing handlers void XMLToolBarFrame::FrameAdd(Ctrl &_parent) { parent = &_parent; parent->AddChild(&toolBarContainer); Layout(); } void XMLToolBarFrame::FrameRemove(void) { toolBars.Clear(); relativePositions.Clear(); parent->RemoveChild(&toolBarContainer); posMapper.Clear(); parent = NULL; } XMLToolBarFrame::XMLToolBarFrame(XMLToolBarState _toolBarState) { parent = NULL; toolBarState = _toolBarState; preDocking = false; } XMLToolBarFrame::~XMLToolBarFrame() { } // find index of a docked XMLToolBarFrame; -1 if not found int XMLToolBarFrame::FindIndex(XMLToolBarCtrl &tb) { int n = toolBars.GetCount(); // skip last toolbar if predocking, it's there // just as a placeholder if(preDocking) n--; for(int i = 0; i < n; i++) if(&tb == toolBars[i]) return i; return -1; } // docks a toolbar into this frame // internal function -- called by XMLToolBar one XMLToolBarFrame &XMLToolBarFrame::Dock(XMLToolBarCtrl &tb, Point p) { // if already docked here, just do nothing if(FindIndex(tb) >= 0) return *this; // undocks/unfloats from previous position asking owning frame, if any, to do it if(tb.toolBarFrame) tb.toolBarFrame->Undock(tb); // dock here the frame toolBars.Add(&tb); toolBarContainer.AddChild(&tb); relativePositions.Add(p); Reposition(); Layout(); tb.toolBarPos = Point(relativePositions.Top().cx, relativePositions.Top().cy); return *this; } bool XMLToolBarFrame::GetDockTarget(XMLToolBarCtrl &tb, Point p, int &dockLine, bool &insert, int &col) { // nothing if point outside frame if(!toolBarContainer.GetScreenRect().Contains(p)) return false; p -= toolBarContainer.GetScreenRect().TopLeft(); Size sz = toolBarContainer.GetRect().GetSize(); // ok, it's inside the frame, we must now search for // row, column and if we must dock on existing row or // inserting a new one int framePos; switch(toolBarState) { case TOOLBAR_LEFT : framePos = p.x; col = p.y; break; case TOOLBAR_RIGHT : framePos = sz.cx - p.x; col = p.y; break; case TOOLBAR_TOP : framePos = p.y; col = p.x; break; case TOOLBAR_BOTTOM : framePos = sz.cy - p.y; col = p.x; break; default : NEVER(); return false; } int numLines = posMapper.GetCount(); if(!numLines) { dockLine = 0; insert = false; return true; } ASSERT(numLines); int lineHeight = frameSize / numLines; ASSERT(lineHeight); dockLine = framePos / lineHeight; int rem = framePos - dockLine * lineHeight; if(rem <= lineHeight / 4) insert = true; else if(rem >= 3 * lineHeight / 4) { dockLine++; insert = true; } else insert = false; return true; } XMLToolBarFrame &XMLToolBarFrame::DockAt(XMLToolBarCtrl &tb, Point p) { // should not happen, but.... if(FindIndex(tb) >= 0) return *this; // get dock position int dockLine, col; bool insert; if(!GetDockTarget(tb, p, dockLine, insert, col)) return *this; // if needed, shift all positions to make place for this toolbar line if(insert) { for(int i = 0; i < relativePositions.GetCount(); i++) if(relativePositions[i].cy >= dockLine) relativePositions[i].cy++; } // docks the toolbar there relativePositions.Add(Size(col, dockLine)); toolBars.Add(&tb); toolBarContainer.AddChild(&tb); Reposition(); Layout(); tb.toolBarPos = Point(relativePositions.Top().cx, relativePositions.Top().cy); return *this; } // closes (undocking it) an XMLToolBar from this frame XMLToolBarFrame &XMLToolBarFrame::Undock(XMLToolBarCtrl &tb) { // if already docked here, just do nothing int i = FindIndex(tb); if(i < 0) return *this; // store inside toolbar last docked position Size &sz = relativePositions[i]; toolBars[i]->toolBarPos= Point(sz.cx, sz.cy); // remove from toolbars and positions list toolBars.Remove(i); toolBarContainer.RemoveChild(&tb); relativePositions.Remove(i); // signals closed toolbar tb.toolBarState = TOOLBAR_CLOSED; tb.toolBarFrame = NULL; // reposition the frame Reposition(); Layout(); // frees toolbar from pointing here return *this; } // pre-docking handling -- Point p in SCREEN COORDINATES XMLToolBarFrame &XMLToolBarFrame::PreDock(XMLToolBarCtrl &tb, Point p) { if(preDocking) return *this; // get dock position int dockLine, col; bool insert; if(!GetDockTarget(tb, p, dockLine, insert, col)) return *this; preDocking = true; // if needed, shift all positions to make place for this toolbar line if(insert) { for(int i = 0; i < relativePositions.GetCount(); i++) if(relativePositions[i].cy >= dockLine) relativePositions[i].cy++; } // docks the toolbar there relativePositions.Add(Size(col, dockLine)); toolBars.Add(&tb); Reposition(); Layout(); return *this; } XMLToolBarFrame &XMLToolBarFrame::UnPreDock(XMLToolBarCtrl &tb) { if(!preDocking) return *this; relativePositions.Trim(relativePositions.GetCount() - 1); toolBars.Trim(toolBars.GetCount() - 1); Reposition(); // parent->Refresh(preDockRect); Layout(); preDocking = false; return *this; } // check whether a point (in SCREEN coordinates) is inside the frame bool XMLToolBarFrame::Contains(Point ps) { if(!this->parent) return false; ps -= parent->GetScreenRect().TopLeft(); return frameRect.Contains(ps); } END_UPP_NAMESPACE