// linewind.c // // This file implements the timeline window's menus, controls and events. // // by John D. de Boer #include "twist.h" #include "twindow.h" extern SortGlobs Sort; extern GlobalOpts Opt; extern StepGlobs Step; extern TimeGlobs Time; extern void *Sel,*Was; extern int MAPWINDS,TIMERATIO; extern float BarRoom; extern window *MapWind[]; extern IBNibRef Nib; const real Breakout2 = 10.0; // break-away distance for beginning a drag int LINEWINDS; // number of timeline windows window *LineWind[MAXLINEWINDS]; // timeline windows int CYCLE; // vertical cycling of bars on timeline page //static string Title; // window title from resource // Menus for the timeline window static MenuRef TimeMenu; static void InitLineMenus(void) { OSStatus err; err=CreateMenuFromNib(Nib,CFSTR("time"),&TimeMenu); } static void SetUpLineMenus(void) { SetUpFileMenu(); InsertMenu(TimeMenu,0); SetUpSortMenu(); SetUpSizeMenu(); InsertFilterMenus(); SetUpWindowMenu(); } static void AdjustLineMenus(window *W) { AdjustFileMenu(W); AdjustFilterMenus(); allowitem(W,'TiFo',Sel!=NULL); allowitem(W,'TiMV',MAPWINDS>0); checkitem(W,'TiMV',Opt.onmap); checkitem(W,'TiEl',Opt.elide); checkitem(W,'TiLi',Opt.thin); checkitem(W,'TiUn',Opt.unc); checkitem(W,'TiIc',Opt.ticon); checkitem(W,'TiFl',Opt.flag); checkitem(W,'TiDy',Opt.dyn); checkitem(W,'TiTi',Opt.ticks); AdjustSortMenu(W); AdjustSizeMenu(W); } static bool LineCommand(window *W, UInt32 C) { if (!W) return 0; switch (C) { case 'TiFo': TimeFocus(); Redisplay(); return 1; case 'TiMV': Opt.onmap=!Opt.onmap; redraw(W); return 1; case 'TiEl': Opt.elide=!Opt.elide; redraw(W); return 1; case 'TiLi': Opt.thin=!Opt.thin; redraw(W); return 1; case 'TiUn': Opt.unc=!Opt.unc; Redisplay(); return 1; case 'TiIc': Opt.ticon=!Opt.ticon; redraw(W); return 1; case 'TiFl': Opt.flag=!Opt.flag; redraw(W); return 1; case 'TiDy': Opt.dyn=!Opt.dyn; redraw(W); return 1; case 'TiTi': Opt.ticks=!Opt.ticks; redraw(W); return 1; } return FileCommand(W,C); } // Contextual Menus static bool TimelinePopup(window *W, point Pt) { if (!Sel) return 0; if (VoidIsPers(Sel)) return PersonPopup(W,Pt,0,1,1,1,Sel); if (VoidIsEvent(Sel)) return EventPopup(W,Pt,0,1,1,Sel); return 0; } // Dragging the time line typedef struct { bool good, // struct has been initialised successfully same, // whether the mouse has remained stationary since the last draw did, // whether any dragging has taken place opt; // whether the option key was held down at the start of the drag window *wind; // time-line window in which drag takes place point start, // start point of drag end; // current or end point of drag real halfw, // half-width of window's page Rect jd; // central day in window (floating-point value of Time.jd) long sel; // selected date TimeGlobs save; // global time variables before drag short cycle; // initial value of cycle } timedragtask; static void inittimedrag(mouseclick *M, timedragtask *T) { real FracRight; if (!M || !M->wind || !T) return; T->wind=M->wind; T->start=T->end=M->point; T->cycle=CYCLE; T->halfw= right(T->wind->page) / 2.0; FracRight= (M->point.x / T->halfw) - 1.0; T->save=Time; T->jd=Time.jd; T->sel= T->jd + (FracRight * Time.dt) + 0.5; T->same=1; T->did=0; T->good=1; T->opt=M->opt; } static void tracktimedragnormal(timedragtask *T, point P) { point Rel; float FracRight,Height; if (!T) return; Rel.x= P.x - T->end.x; Rel.y= P.y - T->end.y; T->same=(Rel.x==0 && Rel.y==0); if (!T->did && (Rel.x * Rel.x) + (Rel.y * Rel.y) <= Breakout2) T->same=1; if (T->same) return; T->end=P; FracRight= (P.x / T->halfw) - 1.0; Height= P.y - T->start.y; CYCLE = T->cycle - (Height / BarRoom); // mod FIT; T->jd= T->sel - (FracRight * Time.dt) + 0.5; T->jd=fbetween(T->jd,Time.jd1,Time.jd2); GoToJD(T->jd); //Time.jd=T->jd; //Step.intv.start.jd=Time.jd; //Step.intv.end.jd= Time.jd + (Time.dt / (real)TIMERATIO) - 1; T->did=1; } static void tracktimedragoption(timedragtask *T, point P) { point Rel; float FracRight,Height,Mag,OldDT,RealSel; if (!T) return; Rel.x= P.x - T->end.x; Rel.y= P.y - T->end.y; T->same=(Rel.x==0 && Rel.y==0); if (T->same) return; T->end=P; FracRight= (P.x / T->halfw) - 1.0; Height= P.y - T->start.y; Mag=exp(Height / 40.0); OldDT=Time.dt; Time.dt=between(T->save.dt / Mag,TIMERATIO,365.25 * 1000.0 * TIMERATIO); T->jd+= FracRight * (OldDT - Time.dt); T->jd=fbetween(T->jd,Time.jd1,Time.jd2); RealSel= T->jd + (FracRight * Time.dt) + 0.5; if (RealSel<0.0) RealSel-=1.0; T->sel=between(RealSel,Time.jd1,Time.jd2); Time.jd=T->jd; Step.intv.start.jd=Time.jd; Step.intv.end.jd= Time.jd + (Time.dt / (real)TIMERATIO) - 1; T->did=1; } static void tracktimedrag(timedragtask *T, point P) { if (!T) return; if (T->opt) tracktimedragoption(T,P); else tracktimedragnormal(T,P); } static bool DragTime(mouseclick *M) { timedragtask T; real DT; long CONTEXTTIME; if (!M) return 0; StopAnim(); T.good=0; inittimedrag(M,&T); if (!T.good) return 0; CONTEXTTIME= TickCount() + GetDblTime(); if (stilldown(M)) tracktimedrag(&T,M->now); if (Sel==Was) redraw(T.wind); else RedisplayAll(); while (stilldown(M)) { tracktimedrag(&T,M->now); if (!T.same) redraw(T.wind); if (!T.did && Sel && TickCount()>CONTEXTTIME) return TimelinePopup(T.wind,T.start); } if (!T.did) return 1; DT=Time.dt; Time=T.save; Time.jd= T.opt ? T.sel : T.jd; jdtodate(Time.jd,&(Time.year),&(Time.month),&(Time.day)); NearestStepSize(DT); if (Opt.focus) MaintainFocus(); Redisplay(); return 1; } // Controls static ControlActionUPP YearUPP, // control action procedure for horizontal scroll bar SpeedUPP; // control action procedure for time-step controls static pascal void yearscroll(ControlRef C, short PART) { window *W; int NEW; if (!C) return; W=controlswindow(C); if (!W) return; if (PART==kControlIndicatorPart) { NEW=GetControlValue(C); if (NEW==0) NEW--; GoToYear(NEW); } else { StopAnim(); TimeSteps(partincrement(PART,1,TIMERATIO)); SetControlValue(C,Time.year); } if (Opt.focus) MaintainFocus(); Redisplay(); } static pascal void speed(ControlRef C, short PART) { window *W; LineInfo *D; UInt32 Mods; bool Shift; if (!C || PART!=TDPart) return; W=controlswindow(C); if (!W) return; D=W->appdata; if (!D) return; Mods=GetCurrentKeyModifiers(); Shift=((Mods & shiftKey)!=0); if (C==D->slow) Slower(Shift); else if (C==D->fast) Faster(Shift); Redisplay(); } static void LineControlsInit(window *W) { LineInfo *D; if (!W) return; D=W->appdata; if (!D) return; D->slow=commonbutton(W,0,projPlus); SetControlAction(D->slow,SpeedUPP); D->fast=commonbutton(W,1,projMinus); SetControlAction(D->fast,SpeedUPP); givehorzscroll(W); SetControlAction(W->hsb,YearUPP); setscrollbar(W->hsb,Time.year,Time.year1,Time.year2); } static bool LineContent(mouseclick *M) { window *W; if (!M) return 0; W=M->wind; if (!W) return 0; Was=Sel; Sel=TimeLineHit(M); if (M->right && Sel) { if (Sel==Was) redraw(W); else RedisplayAll(); return TimelinePopup(W,M->point); } return DragTime(M); } static bool LineDouble(mouseclick *M) { if (!M) return 0; Was=Sel; Sel=TimeLineHit(M); if (Sel && Sel==Was) { SwitchToMap(); return 1; } return LineContent(M); } static bool LineWheel(window *W, short DX, short DY) { int STEP; bool Did; if (!W) return 0; Did=0; if (DX) { STEP= -DX * gtr(Step.days / 15,1); GoToJD(Time.jd + STEP); Did=1; } if (DY) { CYCLE+=DY; Did=1; } Redisplay(); return Did; } bool ArrowKey(keystroke *K) { if (!K) return 0; switch (K->c) { case 28: StopAnim(); TimeSteps(K->shift ? -TIMERATIO : -1); if (Opt.focus) MaintainFocus(); Redisplay(); return 1; case 29: StopAnim(); TimeSteps(K->shift ? TIMERATIO : 1); if (Opt.focus) MaintainFocus(); Redisplay(); return 1; case 30: Slower(K->shift); Redisplay(); return 1; case 31: Faster(K->shift); Redisplay(); return 1; } return 0; } static bool LineKey(keystroke *K) { //UInt8 ch; if (!K) return 0; if (ArrowKey(K)) return 1; //ch=K->c; switch (K->c) { case 8: case 128: if (!Sel) return 0; DeleteFromOrder(Sel); Sel=NULL; Redisplay(); return 1; case '+': Slower(K->shift); Redisplay(); return 1; case '-': Faster(K->shift); Redisplay(); return 1; case ' ': StopAnim(); return 1; } return 0; } static void LineGrow(window *W) { if (!W) return; CYCLE=0; } // Timeline windows void InitLineViews(void) { LINEWINDS=0; //Title=resstring(200); YearUPP=NewControlActionUPP(&yearscroll); SpeedUPP=NewControlActionUPP(&speed); InitLineMenus(); CYCLE=0; } static int IndexOfWindow(window *W) { int i; for (i=0;i=0; } static void OldLineWindow(window *W) { if (!W) return; old(W->appdata); oldwindow(W); } static void CloseLineWindow(window *W) { int i; if (!W) return; i=IndexOfWindow(W); if (i<0) return; OldLineWindow(W); LineWind[i]=LineWind[--LINEWINDS]; Opt.onmap=0; if (!AnimatedWindows()) StopAnim(); } static window *NewLineWindow(void) { window *W; LineInfo *D; Opt.onmap=0; W=standardwindow("Timeline"); if (!W) return NULL; stackwindow(W,StackableWindows()); D=W->appdata=pointer(sizeof(LineInfo)); LineControlsInit(W); installmenus(W,NULL,&SetUpLineMenus,&AdjustLineMenus,&LineCommand); installgrow(W,&LineGrow,NULL); installpaint(W,&DrawTimeLine); installclick(W,&LineContent,&CloseLineWindow,&DrawTimeLine); installdouble(W,&LineDouble); installwheel(W,&LineWheel); installkey(W,&LineKey); installprint(W,&TimeLinePages,&PrintTimeLine); installpict(W,&LinePDFSize,&LineDrawPDF); return W; } void AddLineWindow(void) { window *W; if (LINEWINDS>=MAXLINEWINDS) return; W=NewLineWindow(); if (!W) return; LineWind[LINEWINDS++]=W; revealwindow(W); redraw(W); } // Command-click to change windows void SwitchToTimeLine(void) { window *W; if (!Sel) return; if (LINEWINDS==0) AddLineWindow(); if (LINEWINDS==0) return; W=LineWind[0]; if (!W) return; ShowThisItem(Sel); TimeFocus(); RedisplayAll(); revealwindow(W); }