#include "ET++.ph" #ifdef __GNUG__ #pragma implementation #endif #include "Clipper.h" #include "Class.h" #include "Math.h" #include "View.h" #include "Error.h" #include "Token.h" #include "Window.h" #include "String.h" #include "WindowPort.h" #include "Collection.h" //---- Clipper ----------------------------------------------------------------- NewMetaImpl(Clipper,VObject, (TP(vop), TB(deleteVop), T(minExtent), T(shift), T(relOrigin), T(scale), TP(bgcolor))); Clipper::Clipper(VObject *vp, const Point &minsize, int id, Ink *bg) : VObject(minsize, id) { if (bg == 0) bg= gInkWhite; bgcolor= bg; vop= 0; relOrigin= gPoint0; scale= 1.0; SetMinExtent(minsize); ResetFlag(eVObjOpen); deleteVop= FALSE; Add(vp); } Clipper::~Clipper() { if (vop) { VObject *tmp= vop; vop= 0; tmp->ClearContainer(this); if (deleteVop) delete tmp; } } void Clipper::Open(bool mode) { if (IsOpen() != mode) { VObject::Open(mode); if (vop) vop->Open(mode); } } void Clipper::Add(VObject *vp) { Remove(vop); vop= vp; if (vop) { vop->SetContainer(this); if (IsOpen() && !vop->IsOpen()) vop->Open(TRUE); ViewSizeChanged(); } } VObject *Clipper::Remove(VObject *vp) { if (vop && vop == vp) { vop= 0; if (vp->IsOpen()) vp->Open(FALSE); vp->ClearContainer(this); return vp; } return 0; } void Clipper::SetContainer(VObject *v) { VObject::SetContainer(v); relOrigin= gPoint0; if (vop && !vop->IsKindOf(View)) { vop->CalcExtent(); vop->SetOrigin(gPoint0); } } void Clipper::Enable(bool b, bool redraw) { VObject::Enable(b, redraw); if (vop) vop->Enable(b, redraw); } void Clipper::ScaleUp(Point &p) { p.x= (int) ((float)p.x * scale + 0.5); p.y= (int) ((float)p.y * scale + 0.5); } void Clipper::ScaleDown(Point &p) { p.x= (int) ((float)p.x / scale + 0.5); p.y= (int) ((float)p.y / scale + 0.5); } Point Clipper::GetViewSize() { if (vop) { Point p(vop->GetExtent()-shift); ScaleUp(p); return p; } return gPoint0; } Rectangle Clipper::GetViewedRect() { return Rectangle(relOrigin, contentRect.extent); } void Clipper::SetZoom(float zoom) { scale= zoom; ViewSizeChanged(); ForceRedraw(); } Metric Clipper::GetMinSize() { Point me(minExtent); //Point me(30); if (vop && (minExtent.x == -1 || minExtent.y == -1)) { Point e= vop->GetMinSize().extent; if (minExtent.x == -1) me.x= e.x; if (minExtent.y == -1) me.y= e.y; } //if (vop) // return Metric(me, vop->Base()); return me; } void Clipper::SetMinExtent(const Point &e) { minExtent= e; if (e.x == -1) SetFlag(eVObjHFixed); if (e.y == -1) SetFlag(eVObjVFixed); } void Clipper::ViewSizeChanged() { if (vop) { Point newsize(vop->GetExtent()-shift); ScaleUp(newsize); Send(GetId(), cPartViewSize, &newsize); Normalize(); } } void Clipper::InvalidateViewRect(Rectangle r) { r.origin= ContainerPoint(r.origin); ScaleUp(r.extent); InvalidateRect(r); } void Clipper::SetExtent(Point e) { VObject::SetExtent(e); Normalize(); } void Clipper::SetOrigin(Point at) { VObject::SetOrigin(at); if (vop) vop->SetOrigin(gPoint0); } void Clipper::Normalize() { Point vs(GetViewSize()), ee(GetExtent()), scroll; if (vs.x < ee.x) scroll.x= -relOrigin.x; else if (vs.x < relOrigin.x+ee.x) scroll.x= relOrigin.x+ee.x - vs.x; if (vs.y < ee.y) scroll.y= -relOrigin.y; else if (vs.y < relOrigin.y+ee.y) scroll.y= relOrigin.y+ee.y - vs.y; Scroll(cPartScrollRel, scroll); } void Clipper::Focus() { if (GetContainer()) { GetContainer()->Focus(); GrClipFurther(contentRect); GrTranslate(Offset()); GrScale(scale, scale); } } void Clipper::DrawInner(Rectangle r, bool) { if (bgcolor && bgcolor != ePatNone && !gPrinting) GrPaintRect(r, bgcolor); if (vop) { GrState gs; gs.Use(); GrClipFurther(r); GrTranslate(Offset()); GrScale(scale, scale); r.origin-= Offset(); ScaleDown(r.origin); ScaleDown(r.extent); vop->DrawAll(r, FALSE); } } VObject *Clipper::Detect(BoolFun find, void *arg) { VObject *v; if (vop && (v= vop->Detect(find, arg))) return v; return VObject::Detect(find, arg); } void Clipper::ReadEvent(Token &t) { VObject::ReadEvent(t); t.Pos-= Offset(); ScaleDown(t.Pos); } Command *Clipper::DispatchEvents(Point lp, Token &t, Clipper *vf) { Command *cmd= 0; lp-= Offset(); ScaleDown(lp); if (vop) cmd= vop->Input(lp, t, this); else cmd= VObject::DispatchEvents(lp, t, vf); if (cmd) PerformCommand(cmd); return gNoChanges; } Point Clipper::ContainerPoint(Point p) { ScaleUp(p); return p+Offset(); } void Clipper::SetBgInk(Ink *ip) { bgcolor= ip; ForceRedraw(); } Ink *Clipper::GetBgInk() { return bgcolor; } //---- Scrolling --------------------------------------------------------------- void Clipper::ConstrainScroll(Point *newOrigin) { if (vop && vop->IsKindOf(View)) ((View*)vop)->ConstrainScroll(newOrigin); } Point Clipper::AutoScroll(Point p) { ScaleUp(p); p-= relOrigin; Point scroll= Rectangle(contentRect.extent).Constrain(p); Scroll(cPartScrollRel, scroll-p); return scroll; } void Clipper::RevealRect(Rectangle r, Point minToSee) { r.origin-= shift; ScaleUp(r.extent); ScaleUp(minToSee); VObject::RevealRect(Rectangle(ContainerPoint(r.origin), r.extent), minToSee); ScaleUp(r.origin); Rectangle vr(GetViewedRect()); Rectangle ir(Inter(r, vr)); if (ir.IsEmpty() || ir.extent.x < minToSee.x || ir.extent.y < minToSee.y) Scroll(cPartScrollRel, r.AmountToTranslateWithin(vr)); } void Clipper::RevealAlign(Rectangle r, VObjAlign al) { r.origin-= shift; ScaleUp(r.origin); ScaleUp(r.extent); Rectangle vr(GetViewedRect()); Rectangle ir(Inter(r, vr)); if (ir.extent.x < r.extent.x || ir.extent.y < r.extent.y) { Point scroll; switch (al & eVObjH) { case eVObjHCenter: scroll.x= (vr.Center().x - r.Center().x)/2; break; case eVObjHRight: scroll.x= vr.NE().x - r.NE().x; break; default: scroll.x= vr.NW().x - r.NW().x; break; } switch (al & eVObjV) { case eVObjVCenter: scroll.y= (vr.Center().y - r.Center().y)/2; break; case eVObjVBottom: scroll.y= vr.SW().y - r.SW().y; break; default: scroll.y= vr.NW().y - r.NW().y; break; } Scroll(cPartScrollRel, scroll); } } void Clipper::ExtentChanged(VObject*) { ViewSizeChanged(); } void Clipper::Control(int id, int part, void *val) { if (part == cPartExtentChanged) { if (val == vop) ViewSizeChanged(); } VObject::Control(id, part, val); } void Clipper::SendDown(int id, int part, void *val) { if (vop) vop->SendDown(id, part, val); } Point Clipper::CalcScrollAmount(int mode, const Point &scr, const Rectangle &r) { switch(mode) { case cPartScrollStep: return 32 * -scr; case cPartScrollPage: return (r.extent-32) * -scr; case cPartScrollAbs: return relOrigin - scr; case cPartScrollHAbs: return Point(r.origin.x - scr.x, 0); case cPartScrollVAbs: return Point(0, r.origin.y - scr.y); case cPartScrollRel: return scr; } return gPoint0; } void Clipper::Scroll(int mode, Point scroll, bool redraw) { Point leftCorner, newOrigin; if (GetContainer() == 0) return; Point delta(CalcScrollAmount(mode, scroll, GetViewedRect())); /* switch(mode) { case cPartScrollStep: delta= 32 * -scroll; if (vop && vop->IsKindOf(View)) ((View*)vop)->CalcScrollStep(delta, relOrigin, scroll); break; case cPartScrollPage: delta= (contentRect.extent-32) * -scroll; break; case cPartScrollAbs: delta= relOrigin - scroll; break; case cPartScrollHAbs: delta.x= relOrigin.x - scroll.x; break; case cPartScrollVAbs: delta.y= relOrigin.y - scroll.y; break; case cPartScrollRel: delta= scroll; break; } */ leftCorner= Max(gPoint0, GetViewSize() - contentRect.extent); newOrigin= relOrigin-delta; ConstrainScroll(&newOrigin); delta= relOrigin - Min(Max(gPoint0, newOrigin), leftCorner); if (delta == gPoint0) return; if (! IsOpen()) redraw= FALSE; if (redraw) { UpdateEvent(); Focus(); relOrigin-= delta; ((WindowPort*)GrGetPort())->Scroll(delta); } else relOrigin-= delta; Send(GetId(), cPartScrollPos, &relOrigin); UpdateEvent(); } // returns TRUE on exit loop bool Clipper::TrackOnce(Command** tracker, TrackPhase atp, Point ap, Point pp, Point lp) { Command *newTracker; if (*tracker == 0) { *tracker= gNoChanges; return TRUE; } newTracker= (*tracker)->TrackMouse(atp, ap, pp, lp); if (newTracker != *tracker) { if (*tracker && *tracker != gNoChanges) delete *tracker; *tracker= newTracker; return (newTracker == gNoChanges); } return FALSE; } // called from View void Clipper::DrawInFocus(FeedbackFunc of, Command *tr, const Point &ap, const Point &pp, bool turniton) { if (IsOpen()) { Focus(); of(this, tr, ap, pp, turniton); } } void ClipperFeedback(Clipper *cl, Command *tr, const Point &ap, const Point &pp, bool turniton) { cl->Feedback(tr, ap, pp, turniton); } void Clipper::Feedback(Command *tracker, const Point &ap, const Point &pp, bool turniton) { Ink *pat= ePatBlack; if (tracker->TestFlag(eCmdFullScreen) || ! tracker->TestFlag(eCmdNoReplFeedback)) pat= ePatXor; GrSetInk(pat); GrSetPenInk(pat); GrSetTextInk(pat); GrSetPenSize(1); tracker->TrackFeedback(ap, pp, turniton); UpdateEvent(); } void Clipper::FeedbackOnce(Command *tracker, const Point &ap, const Point &pp, bool turniton) { if (!tracker->TestFlag(eCmdNoReplFeedback) && vop && vop->IsKindOf(View)) ((View*)vop)->ShowInAllClippers(ClipperFeedback, this, tracker, ap, pp, turniton); else Feedback(tracker, ap, pp, turniton); Port::FlushAnyText(); } extern int gTimeOut; Command *Clipper::TrackInContent(Point lp, Token token, Command* tracker) { static int level= 0; static bool haveSeenUp; bool autoscroll= FALSE, havereset, isidle= FALSE, fullscreen= tracker->TestFlag(eCmdFullScreen), done; Point newlp, ap, pp, oldrelorigin; Window *bwin= GetWindow(); level++; if (level == 1) haveSeenUp= FALSE; Focus(); if (fullscreen) bwin->Fullscreen(TRUE); havereset= FALSE; restart2: ap= pp= lp; tracker->TrackConstrain(ap, pp, &lp); ap= pp= lp; if (TrackOnce(&tracker, eTrackPress, ap, pp, lp)) goto out; FeedbackOnce(tracker, ap, pp, TRUE); restart: while (IsOpen()) { if (level > 0 && haveSeenUp && !tracker->TestFlag(eCmdMoveEvents)) break; done= FALSE; while (!done) { if (isidle || autoscroll) { gTimeOut= 0; ReadEvent(token); // poll } else { gTimeOut= 100; ReadEvent(token); // block 100 ms } switch (token.Code) { case -1: if (autoscroll) { token.Code= eEvtLocMove; // simulate a move event done= TRUE; break; } if (tracker->TestFlag(eCmdIdleEvents)) { done= isidle= TRUE; break; } //done= haveSeenUp= TRUE; break; case eEvtRightButton: case eEvtMiddleButton: case eEvtLeftButton: if (token.Flags & eFlgButDown) haveSeenUp= TRUE; else goto restart2; done= TRUE; // return button event immediately break; case eEvtLocMove: done= TRUE; break; default: break; } } lp= token.Pos; if (haveSeenUp) break; FeedbackOnce(tracker, ap, pp, FALSE); // clear previous feedback tracker->TrackConstrain(ap, pp, &lp); if (! fullscreen) { oldrelorigin= relOrigin; newlp= AutoScroll(lp); if (relOrigin != oldrelorigin) { autoscroll= TRUE; lp= newlp + relOrigin; } else autoscroll= FALSE; } oldrelorigin= relOrigin; if (TrackOnce(&tracker, token.Code == eEvtLocMove ? eTrackMove : eTrackIdle, ap, pp, lp)) goto out; pp= lp; FeedbackOnce(tracker, ap, pp, TRUE); // new feedback } FeedbackOnce(tracker, ap, pp, FALSE); // clear last feedback tracker->TrackConstrain(ap, pp, &lp); if (! tracker->TestFlag(eCmdMoveEvents)) { if (fullscreen) bwin->Fullscreen(FALSE); havereset= TRUE; level--; } TrackOnce(&tracker, IsOpen() ? eTrackRelease : eTrackExit, ap, pp, lp); if (tracker->TestFlag(eCmdMoveEvents)) { haveSeenUp= FALSE; goto restart; } out: Focus(); if (! havereset) { if (fullscreen) bwin->Fullscreen(FALSE); level--; } return tracker; } OStream& Clipper::PrintOn(OStream &s) { VObject::PrintOn(s); return s << minExtent SP << scale SP << vop SP << bgcolor SP; } IStream& Clipper::ReadFrom(IStream &s) { VObject::ReadFrom(s); s >> minExtent >> scale >> vop >> bgcolor; Add(vop); SetContentRect(contentRect, TRUE); return s; } void Clipper::InspectorId(char *buf, int sz) { if (vop) vop->InspectorId(buf, sz); else VObject::InspectorId(buf, sz); } void Clipper::CollectParts(Collection* col) { VObject::CollectParts(col); if (vop) col->Add(vop); }