This is eTBookmark.m in view mode; [Download] [Up]
/////////////////////////////////////////////////////////////////////////////// // FILENAME: eTBookmark.m // SUMMARY: Implementation of the "brains" behind eText bookmarks // SUPERCLASS: Object // INTERFACE: None // PROTOCOLS: <Annotation,HTMDSupport,ASCIISupport,LaTeXSupport,Tool, // InspectableTarget> // AUTHOR: Rohit Khare // COPYRIGHT: (c) 1994 California Institure of Technology, eText Project /////////////////////////////////////////////////////////////////////////////// // IMPLEMENTATION COMMENTS // Bookmark creation and destruction are incredibly fragile and complex // timelines, what with consistency, integrity, and bindery checking. // How does this coordinate with eText to find locations, etc? // For HTML conversion, we decided to reduce range-support to point- // support for bookmarks by bracketing AnchorTitle with <A NAME...> /////////////////////////////////////////////////////////////////////////////// // HISTORY // 10/30/94: Modified to support <InspectableTarget> // 06/18/94: Added HTMDSupport. RK & TRZ // 05/08/94: Created. First actual implementation. /////////////////////////////////////////////////////////////////////////////// #import "eTBookmark.h" #import "eTPointmark.h" #define _eTBookmarkVERSION 10 @implementation eTBookmark + toolAwake:theApp { char buf[MAXPATHLEN]; NXBundle *bundle; bundle = [NXBundle bundleForClass:[eTBookmark class]]; if ([bundle getPath:buf forResource:".bmBegin" ofType:"tiff"] ) { [[[NXImage alloc] initFromFile:buf] setName:".bmBegin"]; } else { NXLogError("Image not found: .bmBegin"); } if ([bundle getPath:buf forResource:".bmEnd" ofType:"tiff"] ) { [[[NXImage alloc] initFromFile:buf] setName:".bmEnd"]; } else { NXLogError("Image not found: .bmEnd"); } if ([bundle getPath:buf forResource:".bmCollapsed" ofType:"tiff"] ) { theIcon = [[NXImage alloc] initFromFile:buf]; [theIcon setName:".bmCollapsed"]; } else { NXLogError("Image not found: .bmCollapsed"); } [theApp registerAnnotation: [eTBookmark class] name: "eTBookmark" RTFDirective: "eTBookmark" menuLabel: "Make Bookmark" menuKey: 'B' menuIcon: (NXImage *) theIcon]; [theApp registerAnnotation: [eTBookmarkEnd class] name: "eTBookmarkEnd" RTFDirective: "eTBookmarkEnd" menuLabel: NULL menuKey: 0 menuIcon: (NXImage *) nil]; [eTPointmark toolAwake:theApp]; //Chaining return self; } - init { [super init]; etContainer = etDoc = theEnd = theText = nil; highlighted = collapsed = NO; anchorID = 0; anchorTitle = NXUniqueString("Untitled"); condition = NXUniqueString(""); theIcon = [NXImage findImageNamed:".bmBegin"]; theTextFieldCell = [[TextFieldCell alloc] initTextCell:"Bookmark"]; [[theTextFieldCell setBackgroundGray:NX_BLACK] setTextGray:NX_WHITE]; return self; } - free { // it's not clear that we can expand: recursively in a Text edit. //if (collapsed) NXLogError("AAAAAAAAAAAAAAAAHHHHHHHHHH!!!!!!!!!"); [[NXApp inspector] inspect:nil]; [[eTBookmarkBinder new] unregisterBM:self ID:anchorID inDoc:[[etDoc docInfo] docID]]; [etDoc unregisterNotification:self]; if (etContainer) etContainer = [etContainer free]; if (theEnd) theEnd = [theEnd beginDidFree]; return self = [super free]; } - initFromPboard:thePboard inDoc:theDoc linked:(BOOL) linked { NXSelPt begin, end; [self init]; etDoc = theDoc; anchorID = [NXApp uniqueID]; anchorTitle = NXUniqueString([[theDoc docInfo] docTitle]); [[eTBookmarkBinder new] registerBM:self ID:anchorID inDoc:[[etDoc docInfo] docID]]; [etDoc registerNotification:self]; // create bmEnd theEnd = [[eTBookmarkEnd alloc] init:self ID:anchorID]; // HIGHLY UNSCRUPULOUS KERNEL VIOLATION!!!!! // move sel to end and insert bmEnd into the doc theText = [[theDoc docUI] eTextObj]; [theText getSel:&begin :&end]; // we know the sel exists because // etApp checks acceptsAnnotation: [theText setSel:end.cp :end.cp]; [[theText undoManager] disableUndoRegistration]; [theDoc insertAnnotation:theEnd]; [[theText undoManager] reenableUndoRegistration]; // now move sel to the beginning and return (we will be inserted there) [theText setSel:begin.cp :begin.cp]; // this provides user feedback; if another bookmark is in the Inspector // then this maneuver keeps the bmBrowser updated. [self click:self]; return self; } - calcCellSize:(NXSize *)theSize {[theIcon getSize:theSize]; return self;} - drawSelf:(const NXRect *)cellFrame inView:view // MARGINALIA HERE! { NXPoint point; NXCoord l,r,t,b; NXRect rt; if(!theText) theText = view; PSgsave(); if (highlighted) PSsetgray(NX_LTGRAY); else PSsetgray([view backgroundGray]); NXRectFill(cellFrame); point = cellFrame->origin; point.y += cellFrame->size.height; [theIcon composite:NX_SOVER toPoint:&point]; // Now we stamp the marginalia note // build a rect for the text if (theTextFieldCell) { NXRun *theRun = [theText runForAnnotation:self]; rt = *cellFrame; [theTextFieldCell setFont:theRun->font]; [theTextFieldCell setStringValue:anchorTitle]; NX_Y(&rt) += (NX_HEIGHT(&rt) - 18.0); NX_HEIGHT(&rt) = 18.0; [theText getMarginLeft:&l right:&r top:&t bottom:&b]; NX_WIDTH(&rt) = (l - 8.0); NX_X(&rt) = 4.0; [theTextFieldCell drawSelf:&rt inView:view]; } PSgrestore(); return self; } - highlight:(const NXRect *)cellFrame inView:view lit:(BOOL)flag { if (highlighted != flag) { highlighted = flag; NXHighlightRect(cellFrame); } return self; } #define mask (NX_LMOUSEUPMASK|NX_LMOUSEDRAGGEDMASK) #define Shift(e) (e->flags&(NX_NEXTLSHIFTKEYMASK|NX_NEXTRSHIFTKEYMASK)) #ifndef abs #define abs(x) (((x)<0)? -(x) : (x)) #endif int eTBookmark_mouseMoved(NXPoint *o, int n) { /* true if mouse moves > n pixels from 'o' */ NXEvent *e; NXPoint p; float d; do { e = [NXApp getNextEvent:mask]; p = e->location; d = abs(p.x-o->x); if (d < abs(p.y-o->y)) d = abs(p.y-o->y); } while (e->type != NX_LMOUSEUP && d<n); *o = p; return e->type != NX_LMOUSEUP; } - (NXDragOperation)draggingSourceOperationMaskForLocal:(BOOL)flag { return (flag ? NX_DragOperationLink : NX_DragOperationAll); } - draggedImage:(NXImage *)image endedAt:(NXPoint *)screenPoint deposited:(BOOL)flag { return self; } - (BOOL) trackMouse:(NXEvent *)event inRect:(const NXRect *)cellFrame ofView:controlView { NXPoint hitPoint, preHitPoint; int oldMask; NXEvent saveEvent; Pasteboard *dragPasteboard; NXPoint mouseLocation; mouseLocation = event->location; [controlView convertPoint:&mouseLocation fromView:NULL]; oldMask = [[controlView window] eventMask]; [[controlView window] setEventMask:(oldMask|mask)]; saveEvent = *event; preHitPoint = event->location; hitPoint = event->location; if (NXPointInRect(&mouseLocation, cellFrame)) { if (event->data.mouse.click == 2) [self doubleClick:self]; else if (event->data.mouse.click == 1) { if (eTBookmark_mouseMoved(&hitPoint,4)) { NXRect dragRect; id theTable; char filename[MAXPATHLEN]; NXPoint imgLoc, offset; NXEvent *nextEvent; theTable = [[NXStringTable alloc] init]; [theTable insertKey:DOCID value: NXCopyStringBuffer([[etDoc docInfo] docIDStr])]; sprintf(filename,"%x", anchorID); [theTable insertKey:ANCHORID value:NXCopyStringBuffer(NXUniqueString(filename))]; [theTable insertKey:DOCTITLE value: NXCopyStringBuffer([[etDoc docInfo] docTitle])]; [theTable insertKey:ANCHORTITLE value: NXCopyStringBuffer(anchorTitle)]; sprintf(filename, "/tmp/%s.etfLink", anchorTitle); [theTable writeToFile:filename]; theTable = [[theTable empty] free]; dragPasteboard = [Pasteboard newName: NXDragPboard]; [dragPasteboard declareTypes:&NXFilenamePboardType num:1 owner:nil]; [dragPasteboard writeType: NXFilenamePboardType data: filename length: strlen(filename)]; imgLoc = saveEvent.location; [controlView convertPoint:&imgLoc fromView:nil]; imgLoc.x -= 5; imgLoc.y += 5; nextEvent = [NXApp currentEvent]; offset.x = nextEvent->location.x - saveEvent.location.x; offset.y = nextEvent->location.y - saveEvent.location.y; [controlView dragImage:[NXImage findImageNamed:"NXLinkButton"] at:&imgLoc offset:&offset event:&saveEvent pasteboard:dragPasteboard source:self slideBack: YES]; } else { [self click:self]; } } } [[controlView window] setEventMask:oldMask]; return YES; } - readRichText:(NXStream *)stream forView:view { int i; char buf[MAXPATHLEN]; NXScanf(stream, "%d ", &i); if (i != _eTBookmarkVERSION) { // bad version block. NXLogError("eTBookmark found unparseable version %d at position %d", i, NXTell(stream)); return nil; } if (!theText) theText=view; NXScanf(stream, "%x ", &anchorID); NXScanf(stream, "%d", &i); NXGetc(stream); // space-eater if (i) NXRead(stream, buf, i); buf[i] = 0; anchorTitle = NXUniqueString(buf); NXScanf(stream, "%d", &i); NXGetc(stream); // space-eater if (i) NXRead(stream, buf, i); buf[i] = 0; condition = NXUniqueString(buf); // Register with the Binder! if (!etDoc) etDoc = [view etDoc]; [[eTBookmarkBinder new] registerBM:self ID:anchorID inDoc:[[etDoc docInfo] docID]]; [etDoc registerNotification:self]; return self; } - writeRichText:(NXStream *)stream forView:view { NXPrintf(stream, "%d %x %d %s %d %s", _eTBookmarkVERSION,anchorID, strlen(anchorTitle), anchorTitle, strlen(condition), condition); return self; } - writeASCII:(NXStream *)stream forView:view { NXPrintf(stream, "%y\n", anchorTitle); return self; } - writeASCIIRef:(NXStream *)stream forView:view { NXPrintf(stream, "This is anchor ID# %x", anchorID); return self; } - writeHTML:(NXStream *)stream forView:view { NXPrintf(stream, "<A NAME=\"%x\"><B>%v</B></A>\n", anchorID,anchorTitle); return self; } - writeLaTeX:(NXStream *)stream forView:view { NXPrintf(stream, "{\\em %w}",anchorTitle); return self; } - docWillWrite:etDoc { if (collapsed){ int begin; begin = [theText positionForAnnotation:self]; [theText setSel:(begin+1) :(begin+1)]; [etContainer expand:theText]; reclose = YES; }else reclose = NO; return self; } - docDidWrite:etDoc { // collapse back all the previously collapsed ones if (reclose) { int begin,end; begin = [theText positionForAnnotation:self]; end = [theText positionForAnnotation:theEnd]; if (end == -1) return self; [theText setSel:(begin+1) :(end+1)]; //create a container if (!etContainer) etContainer = [[eTextContainer alloc] init]; [etContainer collapse:theText]; } return self; } - docWillOpen:etDoc { // consult the userModel and collapse out conditioned bookmarks if (condition && *condition && ![[NXApp userModel] boolQuery:condition]) [self collapse]; return self; } - setTitle:(const char *) newTitle { anchorTitle = NXUniqueString(newTitle); [[theText superview] display]; return self; } - setCondition:(const char *) newCondition { condition = NXUniqueString(newCondition); return self; } - collapse { int begin,end; begin = [theText positionForAnnotation:self]; end = [theText positionForAnnotation:theEnd]; if ((begin == -1) || (end == -1)) return self; [theText setSel:(begin+1) :(end+1)]; [theText scrollSelToVisible]; //create a container if (!etContainer) etContainer = [[eTextContainer alloc] init]; [etContainer collapse:theText]; theIcon = [NXImage findImageNamed:".bmCollapsed"]; collapsed = YES; highlighted = NO; [[theText superview] display]; return self; } - expand { int begin; begin = [theText positionForAnnotation:self]; if (begin == -1) {NXBeep(); return self;} [theText setSel:(begin+1) :(begin+1)]; [etContainer expand:theText]; theIcon = [NXImage findImageNamed:".bmBegin"]; collapsed = NO; highlighted = NO; [self highlight:self]; return self; } - click:sender { [[NXApp inspector] inspect:self]; return self; } - (id <Inspectable>) inspectableDelegate { return [[eTBookmarkUI new] setBookmark:self inDoc:[[etDoc docInfo] docID]]; } - doubleClick:sender { if (collapsed) [self expand]; else [self collapse]; return self; } - (NXAtom)title {return anchorTitle;} - (NXAtom)condition {return condition;} - (long)id {return anchorID;} - setEnd:newEnd {theEnd=newEnd; return self;} - endDidFree:sender { // this is the doozie. Suicide? theEnd = nil; return self; } - highlight:sender { int begin,end; if (collapsed) [self expand]; begin = [theText positionForAnnotation:self]; if (begin == -1) {NXBeep(); return self;} end = [theText positionForAnnotation:theEnd]; if (end == -1) { // find eoparagraph and insert an end there end =[theText positionFromLine:([theText lineFromPosition:begin]+1)]-1; if (end < begin) end = begin+1; // create bmEnd theEnd = [[eTBookmarkEnd alloc] init:self ID:anchorID]; [theText setSel:end:end]; [etDoc insertAnnotation:theEnd]; end++; } //[theText display]; [theText setSel:(begin) :(end+1)]; [theText scrollSelToVisible]; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.