#include "XMLToolBarFrame.h" #include "WithXMLMenu.h" NAMESPACE_UPP // recalculates relative toolbar's positions // needed after adding or removing a toolbar void XMLToolBarFrame::Reposition(void) { //DLOG("------------REPOSITIONING --------------------------------"); // 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); //DLOG("Sorted vert positions :"); //for(int ii = 0; ii < vPos.GetCount(); ii++) // DLOG("vPos[" << ii << "] = " << vPos[ii]); 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; XMLToolBar &tb = *toolBars[idx[i]]; Size sz; switch(toolBarState) { case XMLToolBar::TOOLBAR_LEFT : case XMLToolBar::TOOLBAR_RIGHT : sz = tb.GetVertSize(); rowHeight = max(rowHeight, sz.cx); minNextPos = ps[i] + sz.cy + 2; // 2 pixels gap between columns break; case XMLToolBar::TOOLBAR_TOP : case XMLToolBar::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 == XMLToolBar::TOOLBAR_RIGHT || toolBarState == XMLToolBar::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 XMLToolBar::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 XMLToolBar::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 XMLToolBar::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 XMLToolBar::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 Ctrl::LogPos cp( Ctrl::PosSize(frameRect.left, 2), Ctrl::PosSize(frameRect.top, 2) ); toolBarContainer.SetPos(cp, true); // layouts toolbars inside frame Layout(); } void XMLToolBarFrame::FrameAddSize(Size& sz) { Reposition(); switch(toolBarState) { case XMLToolBar::TOOLBAR_LEFT: case XMLToolBar::TOOLBAR_RIGHT: sz.cx += frameSize + 4; break; case XMLToolBar::TOOLBAR_TOP: case XMLToolBar::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]; XMLToolBar &tb = *toolBars[idx]; Ctrl::LogPos pos; Size sz; int x1, y1, x2, y2; switch(toolBarState) { case XMLToolBar::TOOLBAR_LEFT : case XMLToolBar::TOOLBAR_RIGHT : sz = tb.GetVertSize(); x1 = rowPos; y1 = colPos; break; case XMLToolBar::TOOLBAR_TOP : case XMLToolBar::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); // toolBarContainer.SetPos(Ctrl::LogPos(Ctrl::PosSize(2, 2), Ctrl::PosSize(2, 2)), true); Layout(); } void XMLToolBarFrame::FrameRemove(void) { parent->RemoveChild(&toolBarContainer); parent = NULL; } XMLToolBarFrame::XMLToolBarFrame(XMLToolBar::XMLToolBarState _toolBarState) { parent = NULL; toolBarState = _toolBarState; // reset dragging dragging = 0; dragPoint = Point(-1, -1); dragToolBar = NULL; preDocking = false; } XMLToolBarFrame::~XMLToolBarFrame() { } // find index of a docked XMLToolBarFrame; -1 if not found int XMLToolBarFrame::FindIndex(XMLToolBar &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(XMLToolBar &tb, int row, int col) { // 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(Size(col, row)); Reposition(); Layout(); return *this; } bool XMLToolBarFrame::GetDockTarget(XMLToolBar &tb, Point p, int &dockLine, bool &insert, int &col) { // nothing if point outside frame if(!toolBarContainer.GetRect().Contains(p)) return false; p -= toolBarContainer.GetRect().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 XMLToolBar::TOOLBAR_LEFT : framePos = p.x; col = p.y; break; case XMLToolBar::TOOLBAR_RIGHT : framePos = sz.cx - p.x; col = p.y; break; case XMLToolBar::TOOLBAR_TOP : framePos = p.y; col = p.x; break; case XMLToolBar::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::Dock(XMLToolBar &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; //DLOG("------------DOCKING --------------------------------"); //DLOG("p:" << p << " DockLine:" << dockLine << " insert:" << insert << " col:" << col); // 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(); return *this; } // closes (undocking it) an XMLToolBar from this frame XMLToolBarFrame &XMLToolBarFrame::Undock(XMLToolBar &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]->dockedRow = sz.cy; toolBars[i]->dockedCol = sz.cx; // remove from toolbars and positions list toolBars.Remove(i); toolBarContainer.RemoveChild(&tb); relativePositions.Remove(i); // signals closed toolbar tb.toolBarState = XMLToolBar::TOOLBAR_CLOSED; tb.toolBarFrame = NULL; // reposition the frame Reposition(); Layout(); // frees toolbar from pointing here return *this; } // pre-docking handling XMLToolBarFrame &XMLToolBarFrame::PreDock(XMLToolBar &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; //DLOG("PREDOCKING"); // 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(); //DLOG("END PREDOCKING"); // Ctrl::ProcessEvents(); return *this; } XMLToolBarFrame &XMLToolBarFrame::UnPreDock(XMLToolBar &tb) { if(!preDocking) return *this; //DLOG("PREUNDOCKING"); relativePositions.Trim(relativePositions.GetCount() - 1); toolBars.Trim(toolBars.GetCount() - 1); Reposition(); parent->Refresh(preDockRect); Layout(); //DLOG("END PREUNDOCKING"); // Ctrl::ProcessEvents(); preDocking = false; return *this; } // gets toolbar at given mouse point; point is in parent rect coordinates // returns NULL if not grabbed a toolbar XMLToolBar *XMLToolBarFrame::GetToolBarAt(Point p) { p -= toolBarContainer.GetRect().TopLeft(); for(int iToolBar = 0; iToolBar < toolBars.GetCount(); iToolBar++) { XMLToolBar *tb = toolBars[iToolBar]; Rect r = tb->GetRect(); if(r.Contains(p)) return tb; } return NULL; } // check whether a point is inside the frame bool XMLToolBarFrame::Contains(Point p) { return frameRect.Contains(p); } END_UPP_NAMESPACE