#include "ET++.ph" #ifdef __GNUG__ #pragma implementation #endif #include "CollView.h" #include "Class.h" #include "String.h" #include "SeqColl.h" #include "VObject.h" #include "CmdNo.h" #include "Error.h" #include "Math.h" #include "WindowSystem.h" extern bool testopen; //---- CollectionView ---------------------------------------------------------- NewMetaImpl(CollectionView,View, (TP(coll), T(selection), T(gap), T(minExtent), T(rows), T(cols), TP(defaultItem))); CollectionView::CollectionView(EvtHandler *eh, SeqCollection *m, CollViewOptions o, int r, int c) : View(eh, gRect0) { SetFlag(o); defaultItem= new VObject; rows= r; cols= c; SetCollection(m); SetFlag(eVObjKbdFocus); } CollectionView::~CollectionView() { if (coll) { coll->RemoveObserver(this); coll->FreeAll(); SafeDelete(coll); } SafeDelete(xPos); SafeDelete(yPos); SafeDelete(defaultItem); } //---- init methods void CollectionView::SetSelection(const Rectangle &newsel, bool sendcontrol) { if (selection != newsel) { if (IsOpen()) { InvalidateRect(ItemRect(selection)); InvalidateRect(ItemRect(newsel)); } selection= newsel; } if (sendcontrol) DoSelect(selection, WindowSystem::Clicks); } void CollectionView::SetColsRows(const Point &cr) { cols= cr.x; rows= cr.y; Modified(); } void CollectionView::DoSelect(Rectangle r, int clicks) { if (r.IsNotEmpty()) { int partcode= clicks >= 2 ? cPartCollDoubleSelect : cPartCollSelect; Control(GetId(), partcode, (void*) r.origin.y); } } void CollectionView::ClearSelection(bool) { SetSelection(gRect0); } void CollectionView::SetCollection(SeqCollection *m, bool freeold) { if (coll) { coll->RemoveObserver(this); /* { coll->ForEach(VObject,Open)(FALSE); } */ { coll->ForEach(VObject,ClearContainer)(this); } if (freeold) { coll->FreeAll(); delete coll; } } coll= m; if (coll) { coll->assertclass(Meta(VObject)); { coll->ForEach(VObject,SetContainer)(this); } if (IsOpen()) { coll->ForEach(VObject,Open)(TRUE); } coll->AddObserver(this); } selection= gRect0; Modified(); //if (IsOpen()) // Scroll(cPartScrollAbs, gPoint0, FALSE); } void CollectionView::SetDefaultItem(class VObject *d) { if (defaultItem) delete defaultItem; defaultItem= d; } void CollectionView::SetMinExtent(Point e) { if (minExtent != e) { minExtent= e; Modified(); } } Metric CollectionView::GetMinSize() { Update(); return GetExtent(); } void CollectionView::Open(bool m) { View::Open(m); if (testopen) { if (m) { if (coll) { coll->ForEach(VObject,Open)(TRUE); } } else { if (!IsOpen() && coll) { coll->ForEach(VObject,Open)(FALSE); } } } } //---- layout void CollectionView::ConstrainScroll(Point *p) { if (xPos) { bool outside; Point pp(PointToItem(*p, &outside)); if (! outside) p->y= ItemRect(pp).origin.y; } } void CollectionView::CalcScrollStep(Point &delta, const Point &relOrigin, const Point &scroll) { bool outside; Point pp(PointToItem(relOrigin, &outside)+scroll); if (!outside) delta= relOrigin-ItemRect(pp).origin; } void CollectionView::SetOrigin(Point at) { View::SetOrigin(at); if (coll && coll->Size() > 0) { register int x, y; at+= gap; for (x= 0; x < cols; x++) for (y= 0; y < rows; y++) GetItem(x, y)->SetOrigin(Point(xPos[x]+at.x, yPos[y]+at.y)); } } void CollectionView::Update() { register VObject *gop; register int x, y, ww, hh, w, h; int xpos= 0, ypos= 0, sz; Point g; if (!TestFlag(eCVModified)) return; ResetFlag(eCVModified); if (coll == 0 || coll->Size() <= 0) { ForceRedraw(); SetExtent(gPoint1); ForceRedraw(); return; } if (testopen) { { coll->ForEach(VObject,SetContainer)(this); } if (IsOpen()) { coll->ForEach(VObject,Open)(TRUE); } } sz= coll->Size(); if (rows <= 0) SetFlag(eCVExpandRows); if (cols <= 0) SetFlag(eCVExpandCols); if (TestFlag(eCVExpandRows) && TestFlag(eCVExpandCols)) { cols= Math::Sqrt(sz); rows= (sz+cols-1) / cols; } else if (TestFlag(eCVExpandRows)) rows= (sz+cols-1) / cols; else if (TestFlag(eCVExpandCols)) cols= (sz+rows-1) / rows; yPos= (int*) Storage::ReAlloc(yPos, (rows+1) * sizeof(int)); xPos= (int*) Storage::ReAlloc(xPos, (cols+1) * sizeof(int)); xpos= ypos= 0; g= 2*gap; if (TestFlag(eCVGrid)) g+= gPoint1; for (x= 0; x < cols; x++) { xPos[x]= xpos; ww= minExtent.x; for (y= 0; y < rows; y++) { gop= GetItem(x, y); gop->SetContainer(this); if (!testopen) { if (IsOpen()) gop->Open(); } gop->CalcExtent(); w= gop->Width(); ww= Math::Max(ww, w); } xpos+= ww+g.x; } xPos[x]= xpos; for (y= 0; y < rows; y++) { yPos[y]= ypos; hh= minExtent.y; for (x= 0; x < cols; x++) { h= GetItem(x, y)->Height(); hh= Math::Max(hh, h); } ypos+= hh+g.y; } yPos[y]= ypos; for (x= 0; x < cols; x++) for (y= 0; y < rows; y++) GetItem(x, y)->SetContentRect(ItemRect(x, y).Inset(gap), FALSE); if (TestFlag(eCVGrid)) { xpos--; ypos--; } ForceRedraw(); SetExtent(Point(xpos, ypos)); ForceRedraw(); } //---- update void CollectionView::DoObserve(int, int part, void *, Object *op) { if (part == cPartSenderDied) coll= 0; else if (op == coll) Modified(); } void CollectionView::Modified() { SetFlag(eCVModified); if (IsOpen()) Update(); } //---- mapping Point CollectionView::PointToItem(const Point &pos, bool *outside) { register int x, y; register bool out= FALSE; Point p(pos); if (coll == 0 || coll->Size() <= 0) { if (outside) *outside= TRUE; return gPoint0; } Update(); p-= GetOrigin(); if (p.x < xPos[0]) { out= TRUE; x= 0; } else if (p.x >= xPos[cols]) { out= TRUE; x= cols-1; } else { for (x= 0; x < cols; x++) if (p.x >= xPos[x] && p.x < xPos[x+1]) break; } if (p.y < yPos[0]) { out= TRUE; y= 0; } else if (p.y >= yPos[rows]) { out= TRUE; y= rows-1; } else { for (y= 0; y < rows; y++) if (p.y >= yPos[y] && p.y < yPos[y+1]) break; } if (outside) *outside= out; return Point(x, y); } Rectangle CollectionView::ItemRect(const Rectangle &r) { if (r.IsEmpty()) return gRect0; Update(); Rectangle rr(xPos[r.origin.x], yPos[r.origin.y], xPos[r.origin.x+r.extent.x] - xPos[r.origin.x], yPos[r.origin.y+r.extent.y] - yPos[r.origin.y]); if (TestFlag(eCVGrid)) rr.extent-= gPoint1; return rr+GetOrigin(); } Point CollectionView::ItemPos(VObject *g) { VObject *gop; Iter next(coll); Update(); for (int i= 0; gop= (VObject*) next(); i++) if (gop == g) break; if (i >= coll->Size()) return gPoint_1; return Point(i % cols, i / cols); } Rectangle CollectionView::ItemRect(int x, int y) { Update(); x= Math::Range(0, cols-1, x); y= Math::Range(0, rows-1, y); Rectangle r(xPos[x], yPos[y], xPos[x+1]-xPos[x], yPos[y+1]-yPos[y]); if (TestFlag(eCVGrid)) r.extent-= gPoint1; r.origin+= GetOrigin(); return r; } VObject *CollectionView::GetItem(int x, int y) { VObject *vop= 0; int ix= y*cols+x; if (ix >= 0 && ix < coll->Size()) vop= (VObject*)coll->At(ix); return vop ? vop : defaultItem; } //---- event handlung Command *CollectionView::DoLeftButtonDownCommand(Point lp, Token t, int clicks) { if (coll && coll->Size() > 0) { ClearSelection(); return new CellSelector(this, TestFlag(eCVDontStuckToBorder)); } return View::DoLeftButtonDownCommand(lp, t, clicks); } Command *CollectionView::DoKeyCommand(int ch, Token) { register VObject *gop= 0; register int i; int start= 0; Rectangle r= GetViewedRect(); if (r.IsNotEmpty()) { start= r.origin.y+r.extent.y; if (start <= GetExtent().y) start= PointToItem(Point(0,start)).y; } for (i= 0; i < rows; i++) { gop= GetItem(0, (start+i) % rows); const u_char *sort= gStdEncoding->SortMap(); if (gop && (sort[*gop->AsString()] == sort[ch])) break; } if (gop) RevealAlign(ItemRect(Rectangle(ItemPos(gop), gPoint1))); return gNoChanges; } //---- drawing void CollectionView::Draw(Rectangle r) { register int x, y; register VObject *gop; Point p1, p2; if (coll == 0 || coll->Size() <= 0) return; p1= PointToItem(r.NW()); p2= PointToItem(r.SE()+gPoint1); int i= 0; for (x= p1.x; x <= p2.x; x++) { for (y= p1.y; y <= p2.y; y++) { i++; gop= GetItem(x, y); gop->DrawAll(gop->contentRect, selection.ContainsPoint(Point(x, y)) && gop->Enabled()); } } if (TestFlag(eCVGrid)) { GrSetPenNormal(); DrawGrid(p1, p2); } } void CollectionView::DrawGrid(Point p1, Point p2) { register int x, y; int xx= 0, yy= 0; Point o= GetOrigin(); if (p2.x >= cols-1) xx= 1; if (p2.y >= rows-1) yy= 1; for (x= p1.x; x <= p2.x-xx; x++) GrLine(Point(xPos[x+1]-1, yPos[p1.y])+o, Point(xPos[x+1]-1, yPos[p2.y+1]-1)+o); for (y= p1.y; y <= p2.y-yy; y++) GrLine(Point(xPos[p1.x], yPos[y+1]-1)+o, Point(xPos[p2.x+1]-1, yPos[y+1]-1)+o); } //---- input/output OStream& CollectionView::PrintOn(OStream &s) { Object::PrintOn(s); return s << gap SP << minExtent SP << rows SP << cols SP << defaultItem SP << coll SP; } bool CollectionView::PrintOnWhenObserving(Object *from) { return from != coll; } IStream& CollectionView::ReadFrom(IStream &s) { SeqCollection *m; VObject *di= 0; Object::ReadFrom(s); s >> gap >> minExtent >> rows >> cols >> di >> m; SetCollection(m); SetDefaultItem(di); return s; } void CollectionView::CollectParts(Collection* col) { View::CollectParts(col); if (coll && coll->Size() < 12) // hack col->Add(coll); } VObject *CollectionView::Detect(BoolFun find, void *arg) { if (coll) { Iter next(coll); register VObject *v1, *v2; while (v1= (VObject*) next()) if (v2= v1->Detect(find, arg)) return v2; } return 0; } //---- CellSelector ------------------------------------------------------------ CellSelector::CellSelector(CollectionView* v, bool dontstucktoborder) { SetFlag(eCmdNoReplFeedback); lvp= v; last= 0; dontstuck= dontstucktoborder; } void CellSelector::TrackFeedback(Point, Point pp, bool on) { if (on) { if (itemptr == last) { if (last) last->DoTrackMouse((TrackPhase)eTrackMove, pp); } else { if (last) last->DoTrackMouse((TrackPhase)eTrackRelease, pp); if (itemptr) itemptr->DoTrackMouse((TrackPhase)eTrackPress, pp); } last= itemptr; } } Command *CellSelector::TrackMouse(TrackPhase atp, Point, Point, Point np) { bool outside= FALSE; item= lvp->PointToItem(np, &outside); if (outside && dontstuck) itemptr= 0; else itemptr= lvp->GetItem(item.x, item.y); switch (atp) { case eTrackRelease: if (last) last->DoTrackMouse((TrackPhase)eTrackRelease, np); if (itemptr && itemptr->Enabled()) lvp->SetSelection(Rectangle(item, gPoint1), TRUE); else lvp->SetSelection(gRect0, TRUE); case eTrackExit: return gNoChanges; default: break; } return this; }