home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / Apps / DevTools / eText5 / Source / Bookmark.subproj / eTBookmark.m < prev    next >
Encoding:
Text File  |  1995-02-04  |  12.7 KB  |  463 lines

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //    FILENAME:    eTBookmark.m 
  3. //    SUMMARY:    Implementation of the "brains" behind eText bookmarks
  4. //    SUPERCLASS:    Object
  5. //    INTERFACE:    None
  6. //    PROTOCOLS:    <Annotation,HTMDSupport,ASCIISupport,LaTeXSupport,Tool,
  7. //                InspectableTarget>
  8. //    AUTHOR:        Rohit Khare
  9. //    COPYRIGHT:    (c) 1994 California Institure of Technology, eText Project
  10. ///////////////////////////////////////////////////////////////////////////////
  11. //    IMPLEMENTATION COMMENTS
  12. //        Bookmark creation and destruction are incredibly fragile and complex
  13. //    timelines, what with consistency, integrity, and bindery checking.
  14. //        How does this coordinate with eText to find locations, etc?
  15. //        For HTML conversion, we decided to reduce range-support to point-
  16. //    support for bookmarks by bracketing AnchorTitle with <A NAME...>
  17. ///////////////////////////////////////////////////////////////////////////////
  18. //    HISTORY
  19. //    10/30/94:    Modified to support <InspectableTarget>
  20. //    06/18/94:    Added HTMDSupport. RK & TRZ
  21. //    05/08/94:    Created. First actual implementation.
  22. ///////////////////////////////////////////////////////////////////////////////
  23.  
  24. #import "eTBookmark.h"
  25. #import "eTPointmark.h"
  26. #define    _eTBookmarkVERSION    10
  27.  
  28. @implementation eTBookmark
  29.  
  30. + toolAwake:theApp
  31. {
  32.     char        buf[MAXPATHLEN];
  33.     NXBundle    *bundle;
  34.  
  35.     bundle = [NXBundle bundleForClass:[eTBookmark class]];
  36.     if ([bundle getPath:buf forResource:".bmBegin" ofType:"tiff"] ) {
  37.         [[[NXImage alloc] initFromFile:buf] setName:".bmBegin"];
  38.     } else {
  39.         NXLogError("Image not found: .bmBegin");
  40.     }
  41.     if ([bundle getPath:buf forResource:".bmEnd" ofType:"tiff"] ) {
  42.         [[[NXImage alloc] initFromFile:buf] setName:".bmEnd"];
  43.     } else {
  44.         NXLogError("Image not found: .bmEnd");
  45.     }
  46.     if ([bundle getPath:buf forResource:".bmCollapsed" ofType:"tiff"] ) {
  47.         theIcon = [[NXImage alloc] initFromFile:buf];
  48.         [theIcon setName:".bmCollapsed"];
  49.     } else {
  50.         NXLogError("Image not found: .bmCollapsed");
  51.     }
  52.     [theApp   registerAnnotation: [eTBookmark class] 
  53.                             name: "eTBookmark"
  54.                     RTFDirective: "eTBookmark"
  55.                        menuLabel: "Make Bookmark"
  56.                          menuKey: 'B'
  57.                         menuIcon: (NXImage *) theIcon];
  58.     [theApp   registerAnnotation: [eTBookmarkEnd class] 
  59.                             name: "eTBookmarkEnd"
  60.                     RTFDirective: "eTBookmarkEnd"
  61.                        menuLabel: NULL
  62.                          menuKey: 0
  63.                         menuIcon: (NXImage *) nil];
  64.     [eTPointmark toolAwake:theApp]; //Chaining
  65.     return self;
  66. }
  67.  
  68.  
  69. - init 
  70. {
  71.     [super init];
  72.     etContainer = etDoc = theEnd = theText = nil;
  73.     highlighted = collapsed = NO;
  74.     anchorID = 0;
  75.     anchorTitle = NXUniqueString("Untitled");
  76.     condition = NXUniqueString("");
  77.     theIcon = [NXImage findImageNamed:".bmBegin"];
  78.     theTextFieldCell = [[TextFieldCell alloc] initTextCell:"Bookmark"];
  79.     [[theTextFieldCell setBackgroundGray:NX_BLACK] setTextGray:NX_WHITE];
  80.     return self;
  81. }
  82.  
  83. - free
  84. {
  85.     // it's not clear that we can expand: recursively in a Text edit.
  86.     //if (collapsed) NXLogError("AAAAAAAAAAAAAAAAHHHHHHHHHH!!!!!!!!!");
  87.     [[NXApp inspector] inspect:nil];
  88.     [[eTBookmarkBinder new] unregisterBM:self ID:anchorID inDoc:[[etDoc docInfo] docID]];
  89.     [etDoc unregisterNotification:self];
  90.     if (etContainer) etContainer = [etContainer free];
  91.     if (theEnd) theEnd = [theEnd beginDidFree];
  92.     return self = [super free];
  93. }
  94.  
  95. - initFromPboard:thePboard inDoc:theDoc linked:(BOOL) linked
  96. {    
  97.     NXSelPt    begin, end;
  98.  
  99.     [self init];
  100.     etDoc = theDoc;
  101.     anchorID = [NXApp uniqueID];
  102.     anchorTitle = NXUniqueString([[theDoc docInfo] docTitle]);
  103.     [[eTBookmarkBinder new] registerBM:self ID:anchorID inDoc:[[etDoc docInfo] docID]];
  104.     [etDoc registerNotification:self];
  105.     
  106.     // create bmEnd
  107.     theEnd = [[eTBookmarkEnd alloc] init:self ID:anchorID];
  108.     
  109. // HIGHLY UNSCRUPULOUS KERNEL VIOLATION!!!!!    
  110.     // move sel to end and insert bmEnd into the doc
  111.     theText = [[theDoc docUI] eTextObj];
  112.     [theText getSel:&begin :&end];     // we know the sel exists because
  113.                                     // etApp checks acceptsAnnotation:
  114.     [theText setSel:end.cp :end.cp];
  115.     [[theText undoManager] disableUndoRegistration];
  116.     [theDoc insertAnnotation:theEnd];
  117.     [[theText undoManager] reenableUndoRegistration];
  118.     
  119.     // now move sel to the beginning and return (we will be inserted there)
  120.     [theText setSel:begin.cp :begin.cp];
  121.     // this provides user feedback; if another bookmark is in the Inspector
  122.     // then this maneuver keeps the bmBrowser updated.
  123.     [self click:self];
  124.     return self;
  125. }
  126.  
  127. - calcCellSize:(NXSize *)theSize 
  128.     {[theIcon getSize:theSize]; return self;}
  129. - drawSelf:(const NXRect *)cellFrame inView:view        // MARGINALIA HERE!
  130. {
  131.     NXPoint    point; NXCoord l,r,t,b; NXRect rt;
  132.     
  133.     if(!theText) theText = view;
  134.     PSgsave();
  135.     if (highlighted) PSsetgray(NX_LTGRAY);
  136.     else PSsetgray([view backgroundGray]);
  137.     NXRectFill(cellFrame);
  138.     point = cellFrame->origin;
  139.     point.y += cellFrame->size.height;
  140.     [theIcon composite:NX_SOVER toPoint:&point];
  141.     
  142.     // Now we stamp the marginalia note
  143.     // build a rect for the text
  144.     if (theTextFieldCell) {
  145.         NXRun *theRun = [theText runForAnnotation:self];
  146.         rt = *cellFrame;
  147.         
  148.         [theTextFieldCell setFont:theRun->font];
  149.         [theTextFieldCell setStringValue:anchorTitle];
  150.         NX_Y(&rt) += (NX_HEIGHT(&rt) - 18.0);
  151.         NX_HEIGHT(&rt) = 18.0;
  152.         [theText getMarginLeft:&l right:&r top:&t bottom:&b]; 
  153.         NX_WIDTH(&rt)  = (l - 8.0);
  154.         NX_X(&rt) = 4.0;
  155.         [theTextFieldCell drawSelf:&rt inView:view];
  156.     }
  157.     PSgrestore();
  158.     return self;
  159. }
  160. - highlight:(const NXRect *)cellFrame inView:view lit:(BOOL)flag
  161. {
  162.     if (highlighted != flag) {
  163.         highlighted = flag;
  164.         NXHighlightRect(cellFrame);
  165.     }
  166.     return self;
  167. }
  168.  
  169. #define mask (NX_LMOUSEUPMASK|NX_LMOUSEDRAGGEDMASK)
  170. #define Shift(e) (e->flags&(NX_NEXTLSHIFTKEYMASK|NX_NEXTRSHIFTKEYMASK))
  171. #ifndef abs
  172. #define abs(x) (((x)<0)? -(x) : (x))
  173. #endif
  174.  
  175. int eTBookmark_mouseMoved(NXPoint *o, int n) { 
  176.     /* true if mouse moves > n pixels from 'o' */
  177.     NXEvent *e;
  178.     NXPoint p;
  179.     float d;
  180.     do {
  181.         e = [NXApp getNextEvent:mask];
  182.         p = e->location;
  183.         d = abs(p.x-o->x);
  184.         if (d < abs(p.y-o->y)) d = abs(p.y-o->y);
  185.     } while (e->type != NX_LMOUSEUP && d<n);
  186.     *o = p;
  187.     return e->type != NX_LMOUSEUP;
  188. }
  189. - (NXDragOperation)draggingSourceOperationMaskForLocal:(BOOL)flag
  190. {
  191.     return (flag ? NX_DragOperationLink : NX_DragOperationAll);
  192. }
  193. - draggedImage:(NXImage *)image endedAt:(NXPoint *)screenPoint deposited:(BOOL)flag
  194. {
  195.     return self;
  196. }
  197. - (BOOL)    trackMouse:(NXEvent *)event
  198.             inRect:(const NXRect *)cellFrame
  199.             ofView:controlView    
  200. {
  201.     NXPoint    hitPoint, preHitPoint;
  202.     int        oldMask;
  203.     NXEvent    saveEvent;
  204.     Pasteboard    *dragPasteboard;
  205.     NXPoint mouseLocation;
  206.     
  207.     mouseLocation = event->location;
  208.     [controlView convertPoint:&mouseLocation fromView:NULL];
  209.     oldMask = [[controlView window] eventMask];
  210.     [[controlView window] setEventMask:(oldMask|mask)];
  211.     saveEvent = *event;
  212.     preHitPoint = event->location;
  213.     hitPoint = event->location;
  214.  
  215.     if (NXPointInRect(&mouseLocation, cellFrame)) {
  216.         if (event->data.mouse.click == 2)
  217.             [self doubleClick:self];
  218.         else if (event->data.mouse.click == 1) {
  219.             if (eTBookmark_mouseMoved(&hitPoint,4)) {
  220.             NXRect    dragRect;
  221.             id        theTable;
  222.             char    filename[MAXPATHLEN];
  223.             NXPoint    imgLoc, offset;
  224.             NXEvent *nextEvent;
  225.                     
  226.             theTable = [[NXStringTable alloc] init];
  227.             [theTable insertKey:DOCID value:
  228.                 NXCopyStringBuffer([[etDoc docInfo] docIDStr])];
  229.             sprintf(filename,"%x", anchorID);
  230.             [theTable insertKey:ANCHORID value:NXCopyStringBuffer(NXUniqueString(filename))];
  231.             [theTable insertKey:DOCTITLE value:
  232.                 NXCopyStringBuffer([[etDoc docInfo] docTitle])];
  233.             [theTable insertKey:ANCHORTITLE value: 
  234.                 NXCopyStringBuffer(anchorTitle)];
  235.             sprintf(filename, "/tmp/%s.etfLink", anchorTitle);
  236.             [theTable writeToFile:filename];
  237.             theTable = [[theTable empty] free];
  238.  
  239.             dragPasteboard = [Pasteboard newName: NXDragPboard];
  240.           [dragPasteboard declareTypes:&NXFilenamePboardType num:1 owner:nil];
  241.             [dragPasteboard writeType: NXFilenamePboardType data: filename
  242.                 length: strlen(filename)];
  243.             
  244.             imgLoc = saveEvent.location; 
  245.             [controlView convertPoint:&imgLoc fromView:nil];
  246.             imgLoc.x -= 5;
  247.             imgLoc.y += 5;
  248.             
  249.             nextEvent = [NXApp currentEvent];
  250.             offset.x = nextEvent->location.x - saveEvent.location.x;
  251.             offset.y = nextEvent->location.y - saveEvent.location.y;
  252.             
  253.             [controlView
  254.                 dragImage:[NXImage findImageNamed:"NXLinkButton"]
  255.                 at:&imgLoc offset:&offset
  256.                 event:&saveEvent pasteboard:dragPasteboard
  257.                 source:self slideBack: YES];
  258.             } else {
  259.                 [self click:self];
  260.             }
  261.         }
  262.     }
  263.     [[controlView window] setEventMask:oldMask];
  264.     return YES;
  265. }
  266.             
  267. - readRichText:(NXStream *)stream forView:view 
  268. {
  269.     int i;
  270.     char buf[MAXPATHLEN];
  271.     
  272.     NXScanf(stream, "%d ", &i);
  273.     if (i != _eTBookmarkVERSION) {
  274.         // bad version block.
  275.      NXLogError("eTBookmark found unparseable version %d at position %d",
  276.                     i, NXTell(stream));
  277.         return nil;
  278.     }
  279.  
  280.     if (!theText) theText=view;
  281.     NXScanf(stream, "%x ", &anchorID);
  282.  
  283.     NXScanf(stream, "%d", &i); NXGetc(stream); // space-eater
  284.     if (i) NXRead(stream, buf, i);
  285.     buf[i] = 0;
  286.     anchorTitle = NXUniqueString(buf);
  287.  
  288.     NXScanf(stream, "%d", &i); NXGetc(stream); // space-eater
  289.     if (i) NXRead(stream, buf, i);
  290.     buf[i] = 0;
  291.     condition = NXUniqueString(buf);
  292.     
  293.     // Register with the Binder!
  294.     if (!etDoc) etDoc = [view etDoc];
  295.     [[eTBookmarkBinder new] registerBM:self ID:anchorID inDoc:[[etDoc docInfo] docID]];
  296.     [etDoc registerNotification:self];
  297.     
  298.     return self;
  299. }
  300. - writeRichText:(NXStream *)stream forView:view
  301. {
  302.     NXPrintf(stream, "%d %x %d %s %d %s", _eTBookmarkVERSION,anchorID, strlen(anchorTitle), anchorTitle, strlen(condition), condition);
  303.     return self;
  304. }
  305. - writeASCII:(NXStream *)stream forView:view
  306. {
  307.     NXPrintf(stream, "%y\n", anchorTitle);
  308.     return self;
  309. }
  310.  
  311. - writeASCIIRef:(NXStream *)stream forView:view
  312. {
  313.     NXPrintf(stream, "This is anchor ID# %x", anchorID);
  314.     return self;
  315. }
  316. - writeHTML:(NXStream *)stream forView:view
  317. {
  318.     NXPrintf(stream, "<A NAME=\"%x\"><B>%v</B></A>\n", anchorID,anchorTitle);
  319.     return self;
  320. }
  321. - writeLaTeX:(NXStream *)stream forView:view
  322. {
  323.     NXPrintf(stream, "{\\em %w}",anchorTitle);
  324.     return self;
  325. }
  326.  
  327. - docWillWrite:etDoc
  328. {
  329.     if (collapsed){
  330.         int    begin;
  331.         
  332.         begin = [theText positionForAnnotation:self];
  333.         [theText setSel:(begin+1) :(begin+1)];
  334.         [etContainer expand:theText];
  335.         reclose = YES;
  336.     }else reclose = NO;    
  337.     return self;
  338. }
  339.  
  340. - docDidWrite:etDoc
  341. {
  342.     // collapse back all the previously collapsed ones
  343.     if (reclose) {
  344.         int    begin,end;
  345.         
  346.         begin = [theText positionForAnnotation:self];
  347.         end = [theText positionForAnnotation:theEnd];
  348.         if (end == -1) return self;
  349.         [theText setSel:(begin+1) :(end+1)];
  350.         //create a container 
  351.         if (!etContainer) etContainer = [[eTextContainer alloc] init];
  352.         [etContainer collapse:theText];
  353.     }
  354.     return self;
  355. }
  356.  
  357. - docWillOpen:etDoc
  358. {
  359.     // consult the userModel and collapse out conditioned bookmarks
  360.     if (condition && *condition && ![[NXApp userModel] boolQuery:condition])
  361.         [self collapse];
  362.     return self;
  363. }
  364.  
  365. - setTitle:(const char *) newTitle
  366. {
  367.     anchorTitle = NXUniqueString(newTitle);
  368.     [[theText superview] display];
  369.     return self;
  370. }
  371.  
  372. - setCondition:(const char *) newCondition
  373. {
  374.     condition = NXUniqueString(newCondition);
  375.     return self;
  376. }
  377.  
  378. - collapse
  379. {
  380.     int    begin,end;
  381.     
  382.     begin = [theText positionForAnnotation:self];
  383.     end = [theText positionForAnnotation:theEnd];
  384.     if ((begin == -1) || (end == -1)) return self;
  385.     [theText setSel:(begin+1) :(end+1)];
  386.     [theText scrollSelToVisible];
  387.     //create a container 
  388.     if (!etContainer) etContainer = [[eTextContainer alloc] init];
  389.     [etContainer collapse:theText];
  390.     theIcon = [NXImage findImageNamed:".bmCollapsed"];
  391.     collapsed = YES;
  392.     highlighted = NO;
  393.     [[theText superview] display];
  394.     return self;
  395. }
  396.  
  397. - expand
  398. {
  399.     int    begin;
  400.     
  401.     begin = [theText positionForAnnotation:self];
  402.     if (begin == -1) {NXBeep(); return self;}
  403.     [theText setSel:(begin+1) :(begin+1)];
  404.     [etContainer expand:theText];
  405.     theIcon = [NXImage findImageNamed:".bmBegin"];
  406.     collapsed = NO;
  407.     highlighted = NO;
  408.     [self highlight:self];
  409.     return self;
  410. }
  411.  
  412. - click:sender
  413. {
  414.     [[NXApp inspector] inspect:self];
  415.     return self;
  416. }
  417. - (id <Inspectable>) inspectableDelegate {
  418.     return [[eTBookmarkUI new] setBookmark:self inDoc:[[etDoc docInfo] docID]]; }
  419.  
  420. - doubleClick:sender
  421. {
  422.     if (collapsed) [self expand];
  423.     else [self collapse];
  424.     return self;
  425. }
  426.  
  427. - (NXAtom)title {return anchorTitle;}
  428. - (NXAtom)condition {return condition;}
  429. - (long)id {return anchorID;}
  430. - setEnd:newEnd {theEnd=newEnd; return self;}
  431.  
  432. - endDidFree:sender
  433. {
  434.     // this is the doozie. Suicide?
  435.     theEnd = nil;
  436.     return self;
  437. }
  438.  
  439. - highlight:sender
  440. {
  441.     int    begin,end;
  442.     
  443.     if (collapsed) [self expand];
  444.     begin = [theText positionForAnnotation:self];
  445.     if (begin == -1) {NXBeep(); return self;}
  446.     end = [theText positionForAnnotation:theEnd];
  447.     if (end == -1) {
  448.         // find eoparagraph and insert an end there
  449.         end =[theText positionFromLine:([theText lineFromPosition:begin]+1)]-1;
  450.         if (end < begin) end = begin+1;
  451.         // create bmEnd
  452.         theEnd = [[eTBookmarkEnd alloc] init:self ID:anchorID];
  453.         [theText setSel:end:end];
  454.         [etDoc insertAnnotation:theEnd];
  455.         end++;
  456.     }
  457.     //[theText display];
  458.     [theText setSel:(begin) :(end+1)];
  459.     [theText scrollSelToVisible];
  460.     return self;
  461. }
  462.  
  463. @end