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;
}
@endThese are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.