// twhich.c // // by John D. de Boer #include "twist.h" extern TimeGlobs Time; extern int PEOPLE,EVENTS; extern Person ***People; extern Event ***Events; extern short FILTPREFS; extern bool *FiltPrefs; extern int PITEMS,EITEMS; extern IBNibRef Nib; SortGlobs Sort; // user preferences for showing & sorting items in "Order" int ORDER; // number of historical things currently selected void ***Order; // the people & events int ITEMS; // the number of items in both menus FilterItem ***Items; // the items MenuRef PeopleMenu, // the "People" menu EventMenu; // the "Events" menu static int NEXTSUBID; // The "All" and "None" strings static string PAN,EAN; static void InitAllAndNone(void) { string All,None; All=menuitemstring(PeopleMenu,1); if (!All) All=copyof("All"); None=menuitemstring(PeopleMenu,2); if (!None) None=copyof("None"); PAN=cat4(10,"-;",All,";",None); All=menuitemstring(EventMenu,1); if (!All) All=copyof("All"); None=menuitemstring(EventMenu,2); if (!None) None=copyof("None"); EAN=cat4(10,"-;",All,";",None); } // Creating menu items from .ttt files static void AddItem(FilterItem *F) { int NEW; if (!F) return; NEW=ITEMS; ITEMS++; minimumsize(Items,ITEMS,sizeof(FilterItem *)); (*Items)[NEW]=F; } FilterItem *NewFilterItem(void) { FilterItem *F; F=pointer(sizeof(FilterItem)); F->people=1; F->name=NULL; F->num=0; F->place=-1; F->item=(SubItem ****)handle(0); F->id=-1; F->menu=NULL; AddItem(F); return F; } static void AddSubItem(FilterItem *F) { SubItem **H; int NEW; if (!F) return; H=(SubItem **)handle(sizeof(SubItem)); (*H)->name=NULL; (*H)->show=1; (*H)->num=0; NEW=F->num; F->num++; minimumsize(F->item,F->num,sizeof(SubItem **)); (*(F->item))[NEW]=H; } void FilterItemName(FilterItem *F, char *S) { SubItem **H; if (!F || !S) return; if (!F->name) F->name=copyof(S); if (F->num>0) H=(*(F->item))[F->num - 1]; else H=NULL; if (!H || F->num>1 || (*H)->num>0) AddSubItem(F); if (F->num<1) return; H=(*(F->item))[F->num - 1]; if ((*H)->name) old((*H)->name); (*H)->name=copyof(S); } void FilterItemOff(FilterItem *F) { SubItem **H; if (!F || F->num<1) return; H=(*(F->item))[F->num - 1]; if (H) (*H)->show=0; } static void AddMember(SubItem **H, int MBR) { int NEW; if (!H) return; NEW=(*H)->num; (*H)->num++; resizehandle(H,sizeof(SubItem) + ((*H)->num * sizeof(short))); (*H)->mbr[NEW]=MBR; } void FilterItemMember(FilterItem *F, char *S) { SubItem **H; int MBR; if (!F || F->num<1 || !S) return; H=(*(F->item))[F->num - 1]; if (!H) return; MBR= F->people ? NewOccupation(S) : NewType(S); if (!(*H)->show) { if (F->people) OccShow(MBR,0); else TypeShow(MBR,0); } AddMember(H,MBR); } static int LastMember(FilterItem *F) { SubItem **H; if (!F || F->num<1) return -1; H=(*(F->item))[F->num - 1]; if (!H || (*H)->num<1) return -1; return (*H)->mbr[(*H)->num - 1]; } void FilterItemMemberIcon(FilterItem *F, picture P) { int MBR; if (!F || !P) return; MBR=LastMember(F); if (MBR<0) return; if (F->people) SetOccIcon(MBR,P); else SetTypeIcon(MBR,P); } void FilterItemMemberSynonym(FilterItem *F, char *S) { int MBR; if (!F || !S) return; MBR=LastMember(F); if (MBR<0) return; if (F->people) SetOccSynonym(MBR,S); else SetTypeSynonym(MBR,S); } void FilterItemMemberFemale(FilterItem *F) { int MBR; if (!F) return; MBR=LastMember(F); if (MBR<0) return; if (F->people) SetOccFemale(MBR); } void FilterItemMemberColour(FilterItem *F, colour *C) { int MBR; if (!F || !C) return; MBR=LastMember(F); if (MBR<0) return; if (!F->people) SetTypeColour(MBR,C); } void VerifyNewFilterItem(FilterItem *F) { int i; SubItem **H; if (!F || F->num<=1) return; F->id=NEXTSUBID++; F->menu=NewMenu(F->id,"\pYou won't see this"); for (i=0;inum;i++) { H=(*(F->item))[i]; addmenuitems(F->menu,(*H)->name); } addmenuitems(F->menu,F->people ? PAN : EAN); } // This function returns a date for sorting items. In the event of a tie, e.g. // between two books by one author who is shown, a tie-breaker is returned. static long VoidToJD(const void *V, int TIES) { Person *P; Event *E; Day *D; if (!V) return 0; if (VoidIsPers(V)) { P=(Person *)V; switch (TIES) { case 0: if (P->nrgn>0 && Sort.office) { D=&(P->reign[0].start); if (D->kind==timeJD) return D->jd - (365 * 40); } return P->life.start.jd; case 1: return P->life.end.jd; default: return P->life.start.jd; } } if (VoidIsEvent(V)) { E=(Event *)V; P= Sort.group ? E->pers : NULL; if (P && !P->show) P=NULL; if (P) { if (TIES<2) return VoidToJD(P,TIES); else TIES-=2; } switch (TIES) { case 0: return E->date.start.jd; case 1: return E->date.end.jd; default: return E->date.start.jd; } } return Time.jd1; } // These functions compute the list of items which are to be shown according // to the user filters, and puts them in chronological order. static bool ShowPerson(Person *P) { int i; if (!P) return 0; for (i=0;inocc;i++) if (ShowOcc(P->occ[i])) return 1; return 0; } static bool ShowEvent(Event *E) { int i; Person *P; if (!E) return 0; P=E->pers; if (Sort.assoc && P && P->show) return 1; for (i=0;intype;i++) if (ShowType(E->type[i])) return 1; return 0; } static bool InOverallOrder(const void *A, const void *B) { int i; long JD1,JD2; if (!A || !B) return 0; for (i=0;i<3;i++) { JD1=VoidToJD(A,i); JD2=VoidToJD(B,i); if (JD1JD2) return 0; } return 1; } void SetUpOrder(void) { int i; ORDER=0; minimumsize(Order,PEOPLE + EVENTS,sizeof(void *)); for (i=0;ishow=ShowPerson(P); P->sorttime=VoidToJD(P,0); if (P->show) (*Order)[ORDER++]=P; } for (i=0;ishow=ShowEvent(E); E->sorttime=VoidToJD(E,0); if (E->show) (*Order)[ORDER++]=E; } lock(Order); sort(*Order,ORDER,&InOverallOrder); unlock(Order); } void DeleteFromOrder(void *V) { int i; bool Found; void **A; if (!V) return; lock(Order); A=*Order; Found=0; for (i=0;inum==1) DoFilterItem(F,Show[0]); else for (i=0;inum;i++) DoFilterSubMenu(F,i + 1,Show[i]); } static void UseFiltPrefs(void) { int i,NF; FilterItem *F; NF=0; for (i=0;inum; } } void MakeMenus(void) { int i,j,NF,NComd; FilterItem *F; MenuRef M; OSErr err; PurgeMenus(); NF=0; NComd=0; for (i=0;ipeople ? PeopleMenu : EventMenu; addmenuitems(M,F->name); F->place= (F->people) ? (++PITEMS) : (++EITEMS); if (F->menu) { setsubmenu(M,F->place,F->id); for (j=0;jnum;j++) err=SetMenuItemCommandID(F->menu,j + 1,'Item' + (NComd++)); err=SetMenuItemCommandID(F->menu,F->num + 2,'Item' + (NComd++)); err=SetMenuItemCommandID(F->menu,F->num + 3,'Item' + (NComd++)); } else SetMenuItemCommandID(M,F->place,'Item' + (NComd++)); NF+=F->num; } addmenuitems(PeopleMenu,PAN); err=SetMenuItemCommandID(PeopleMenu,PITEMS + 2,'Item' + (NComd++)); err=SetMenuItemCommandID(PeopleMenu,PITEMS + 3,'Item' + (NComd++)); addmenuitems(EventMenu,EAN); err=SetMenuItemCommandID(EventMenu,EITEMS + 2,'Item' + (NComd++)); err=SetMenuItemCommandID(EventMenu,EITEMS + 3,'Item' + (NComd++)); if (NF==FILTPREFS) UseFiltPrefs(); SetUpOrder(); } // Saving filter preferences in an array static int SOFAR; // members of "FiltPrefs" written to so far static int CountItemFilters(FilterItem *F) { if (!F) return 0; return F->num; } static int CountFilters(void) { int i,N; N=0; for (i=0;inum;i++) { H=(*(F->item))[i]; if (range(SOFAR,FILTPREFS)) FiltPrefs[SOFAR++]=(*H)->show; } } void SaveWhichInArray(void) { int i; FILTPREFS=CountFilters(); old(FiltPrefs); FiltPrefs=pointer(FILTPREFS * sizeof(bool)); SOFAR=0; for (i=0;iname); release(H); } static void OldItem(FilterItem *F) { int i; if (!F) return; for (i=0;inum;i++) OldSubItem((*(F->item))[i]); release(F->item); old(F->name); if (F->menu) { DeleteMenu(F->id); DisposeMenu(F->menu); } old(F); } void PurgeWhich(void) { PurgeOrder(); SaveWhichInArray(); while (ITEMS>0) OldItem((*Items)[--ITEMS]); PurgeMenus(); NEXTSUBID=FIRSTSUBID; addmenuitems(PeopleMenu,PAN); addmenuitems(EventMenu,EAN); } // Using filters void AdjustFilterMenu(FilterItem *F) { int i; SubItem **H; if (!F || !F->menu) return; for (i=0;inum;i++) { H=(*(F->item))[i]; CheckMenuItem(F->menu,i + 1,(*H)->show); } } static void DoFilterSubItem(SubItem **H, int SHOW, bool People) { int i,MBR; if (!H || !*H) return; if (SHOW<0) SHOW=!(*H)->show; (*H)->show=SHOW; for (i=0;i<(*H)->num;i++) { MBR=(*H)->mbr[i]; if (People) OccShow(MBR,SHOW); else TypeShow(MBR,SHOW); } } void DoFilterSubMenu(FilterItem *F, int ITEM, int SHOW) { int i; if (!F || !F->menu) return; if (range(ITEM - 1,F->num)) DoFilterSubItem((*(F->item))[ITEM - 1],SHOW,F->people); if (ITEM == F->num + 2) for (i=0;inum;i++) DoFilterSubItem((*(F->item))[i],1,F->people); if (ITEM == F->num + 3) for (i=0;inum;i++) DoFilterSubItem((*(F->item))[i],0,F->people); } void DoFilterItem(FilterItem *F, int SHOW) { SubItem **H; if (!F || !F->item) return; if (F->num==1) { H=(*(F->item))[0]; DoFilterSubItem(H,SHOW,F->people); return; } if (!F->menu || SHOW<0) return; DoFilterSubMenu(F,F->num + (SHOW ? 2 : 3),0); } // These procedures are used to show items in the filtered (i.e. timeline and map) windows when they // are selected in one of the windows that show all people/events (e.g. genealogy). static void ShowMembersItem(int MBR, bool People) { int i,j,k; FilterItem *F; SubItem **H; for (i=0;ipeople!=People) continue; for (j=0;jnum;j++) { H=(*(F->item))[j]; if (!H) continue; for (k=0;k<(*H)->num;k++) if ((*H)->mbr[k]==MBR) { DoFilterSubItem(H,1,People); return; } } } } static void ShowThisPerson(Person *P) { if (!P || P->show || P->nocc<1) return; ShowMembersItem(P->occ[0],1); } static void ShowThisEvent(Event *E) { if (!E || E->show || E->ntype<1) return; ShowMembersItem(E->type[0],0); } void ShowThisItem(void *V) { if (!V) return; if (VoidIsPers(V)) ShowThisPerson((Person *)V); else if (VoidIsEvent(V)) ShowThisEvent((Event *)V); SetUpOrder(); }