Annotation of Examples/AppKit/Draw/TextGraphic.m, revision 1.1

1.1     ! root        1: #import "draw.h"
        !             2: 
        !             3: @implementation TextGraphic
        !             4: /*
        !             5:  * This uses a text object to draw and edit text.
        !             6:  *
        !             7:  * The one quirky thing to understand here is that growable Text objects
        !             8:  * in NeXTSTEP must be subviews of flipped view.  Since a GraphicView is not
        !             9:  * flipped, we must have a flipped view into the view heirarchy when we
        !            10:  * edit (this editing view is permanently installed as a subview of the
        !            11:  * GraphicView--see GraphicView's newFrame: method).
        !            12:  */
        !            13: 
        !            14: + initialize
        !            15: {
        !            16:     [TextGraphic setVersion:6];        /* class version, see read: */
        !            17:     return self;
        !            18: }
        !            19: 
        !            20: static Text *drawText = nil;   /* shared Text object used for drawing */
        !            21: 
        !            22: static void initClassVars()
        !            23: /*
        !            24:  * Create the class variable drawText here.
        !            25:  */
        !            26: {
        !            27:     if (!drawText) {
        !            28:        drawText = [Text new];
        !            29:        [drawText setMonoFont:NO];
        !            30:        [drawText setEditable:NO];
        !            31:        [drawText setSelectable:NO];
        !            32:        [drawText setFlipped:YES];
        !            33:     }
        !            34: }
        !            35: 
        !            36: + (BOOL)canInitFromPasteboard:(Pasteboard *)pboard
        !            37: {
        !            38:     return IncludesType([pboard types], NXRTFPboardType) ||
        !            39:           IncludesType([pboard types], NXAsciiPboardType);
        !            40: }
        !            41: 
        !            42: - init
        !            43: /*
        !            44:  * Creates a "blank" TextGraphic.
        !            45:  * This is TextGraphic's designated initializer,
        !            46:  * but be wary because by the time this returns, the
        !            47:  * TextGraphic may not be full initialized (it'll be
        !            48:  * valid, just perhaps not fully initialized).
        !            49:  * Override finishedWithInit if you want that.
        !            50:  */
        !            51: {
        !            52:     initClassVars();
        !            53:     [super init];
        !            54:     return self;
        !            55: }
        !            56: 
        !            57: - initEmpty
        !            58: /*
        !            59:  * Creates an empty TextGraphic.
        !            60:  */
        !            61: {
        !            62:     [self init];
        !            63:     return [self finishedWithInit];
        !            64: }
        !            65: 
        !            66: - finishedWithInit
        !            67: /*
        !            68:  * Override this if you want to know when a newly
        !            69:  * initialized TextGraphics is fully init'ed.
        !            70:  */
        !            71: {
        !            72:     return self;
        !            73: }
        !            74: 
        !            75: - doInitFromStream:(NXStream *)stream
        !            76: /*
        !            77:  * Common code for initFromStream: and reinitFromStream:.
        !            78:  * Looks at the first 5 characters of the stream and if it
        !            79:  * looks like an RTF file, then the contents of the stream
        !            80:  * are parsed as RTF, otherwise, the contents of the stream
        !            81:  * are assumed to be ASCII text and is passed through the
        !            82:  * drawText object and turned into RTF (using the method
        !            83:  * (writeRichText:).
        !            84:  */
        !            85: {
        !            86:     int maxlen;
        !            87:     char *buffer;
        !            88: 
        !            89:     if (stream) {
        !            90:        NXGetMemoryBuffer(stream, &buffer, &length, &maxlen);
        !            91:        if (!strncmp(buffer, "{\\rtf", 5)) {
        !            92:            NX_ZONEMALLOC([self zone], data, char, length);
        !            93:            bcopy(buffer, data, length);
        !            94:            [drawText readRichText:stream];
        !            95:        } else {
        !            96:            [drawText selectAll:self];
        !            97:            [drawText setFont:[Font userFontOfSize:-1.0 matrix:NX_FLIPPEDMATRIX]];
        !            98:            [drawText readText:stream];
        !            99:            stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
        !           100:            [drawText writeRichText:stream];
        !           101:            NXGetMemoryBuffer(stream, &buffer, &length, &maxlen);
        !           102:            NX_ZONEMALLOC([self zone], data, char, length);
        !           103:            bcopy(buffer, data, length);
        !           104:            NXCloseMemory(stream, NX_FREEBUFFER);
        !           105:        }
        !           106:        [drawText setSel:0 :0];
        !           107:        font = [drawText font];
        !           108:     }
        !           109: 
        !           110:     return self;
        !           111: }
        !           112: 
        !           113: - initFromStream:(NXStream *)stream
        !           114: /*
        !           115:  * Initializes the TextGraphic using data from the passed stream.
        !           116:  */
        !           117: {
        !           118:     [self init];
        !           119: 
        !           120:     if (stream) {
        !           121:        [self doInitFromStream:stream];
        !           122:        [drawText setHorizResizable:YES];
        !           123:        [drawText setVertResizable:YES];
        !           124:        bounds.size.width = bounds.size.height = 10000.0;
        !           125:        [drawText setMaxSize:&bounds.size];
        !           126:        [drawText calcLine];
        !           127:        [drawText getMinWidth:&bounds.size.width minHeight:&bounds.size.height maxWidth:10000.0 maxHeight:10000.0];
        !           128:        bounds.origin.x = bounds.origin.y = 0.0;
        !           129:     }
        !           130: 
        !           131:     return [self finishedWithInit];
        !           132: }
        !           133: 
        !           134: - initFromFile:(const char *)file
        !           135: /*
        !           136:  * Initializes the TextGraphic using data from the passed file.
        !           137:  */
        !           138: {
        !           139:     TextGraphic *retval = nil;
        !           140:     NXStream *stream = NXMapFile(file, NX_READONLY);
        !           141:     retval = [self initFromStream:stream];
        !           142:     NXCloseMemory(stream, NX_FREEBUFFER);
        !           143:     return [retval finishedWithInit];
        !           144: }
        !           145: 
        !           146: 
        !           147: - initFromPasteboard:(Pasteboard *)pboard
        !           148: /*
        !           149:  * Initializes the TextGraphic using data from the passed Pasteboard.
        !           150:  */
        !           151: {
        !           152:     NXStream *stream;
        !           153: 
        !           154:     if (IncludesType([pboard types], NXRTFPboardType)) {
        !           155:        stream = [pboard readTypeToStream:NXRTFPboardType];
        !           156:        [self initFromStream:stream];
        !           157:        NXCloseMemory(stream, NX_FREEBUFFER);
        !           158:     } else if (IncludesType([pboard types], NXAsciiPboardType)) {
        !           159:        stream = [pboard readTypeToStream:NXAsciiPboardType];
        !           160:        [self initFromStream:stream];
        !           161:        NXCloseMemory(stream, NX_FREEBUFFER);
        !           162:     } else {
        !           163:        [self free];
        !           164:        return nil;
        !           165:     }
        !           166: 
        !           167:     return [self finishedWithInit];
        !           168: }
        !           169: 
        !           170: - (NXRect)reinitFromStream:(NXStream *)stream
        !           171: /*
        !           172:  * Reinitializes the TextGraphic from the data in the passed stream.
        !           173:  */
        !           174: {
        !           175:     NXRect ebounds;
        !           176:     [self doInitFromStream:stream];
        !           177:     [self getExtendedBounds:&ebounds];
        !           178:     return ebounds;
        !           179: }
        !           180: 
        !           181: - (NXRect)reinitFromFile:(const char *)file
        !           182: /*
        !           183:  * Reinitializes the TextGraphic from the data in the passed file.
        !           184:  */
        !           185: {
        !           186:     NXRect ebounds;
        !           187:     NXStream *stream = NXMapFile(file, NX_READONLY);
        !           188:     [self doInitFromStream:stream];
        !           189:     NXCloseMemory(stream, NX_FREEBUFFER);
        !           190:     [self getExtendedBounds:&ebounds];
        !           191:     return ebounds;
        !           192: }
        !           193: 
        !           194: - (NXRect)reinitFromPasteboard:(Pasteboard *)pboard
        !           195: /*
        !           196:  * Reinitializes the TextGraphic from the data in the passed Pasteboard.
        !           197:  */
        !           198: {
        !           199:     NXRect ebounds;
        !           200:     NXStream *stream;
        !           201: 
        !           202:     if (IncludesType([pboard types], NXRTFPboardType)) {
        !           203:        stream = [pboard readTypeToStream:NXRTFPboardType];
        !           204:        [self doInitFromStream:stream];
        !           205:        [self getExtendedBounds:&ebounds];
        !           206:        NXCloseMemory(stream, NX_FREEBUFFER);
        !           207:     } else if (IncludesType([pboard types], NXAsciiPboardType)) {
        !           208:        stream = [pboard readTypeToStream:NXAsciiPboardType];
        !           209:        [self doInitFromStream:stream];
        !           210:        [self getExtendedBounds:&ebounds];
        !           211:        NXCloseMemory(stream, NX_FREEBUFFER);
        !           212:     } else {
        !           213:        ebounds.origin.x = ebounds.origin.y = 0.0;
        !           214:        ebounds.size.width = ebounds.size.height = 0.0;
        !           215:     }
        !           216: 
        !           217:     return ebounds;
        !           218: }
        !           219: 
        !           220: - free
        !           221: {
        !           222:     free(data);
        !           223:     return [super free];
        !           224: }
        !           225: 
        !           226: /* Link methods */
        !           227: 
        !           228: - setLink:(NXDataLink *)aLink
        !           229: /*
        !           230:  * Note that we "might" be linked because even though we obviously
        !           231:  * ARE linked now, that might change in the future and the mightBeLinked
        !           232:  * flag is only advisory and is never cleared.  This is because during
        !           233:  * cutting and pasting, the TextGraphic might be linked, then unlinked,
        !           234:  * then linked, then unlinked and we have to know to keep trying to
        !           235:  * reestablish the link.  See readLinkForGraphic:... in gvLinks.m.
        !           236:  */
        !           237: {
        !           238:     NXDataLink *oldLink = link;
        !           239:     link = aLink;
        !           240:     gFlags.mightBeLinked = YES;
        !           241:     return oldLink;
        !           242: }
        !           243: 
        !           244: - (NXDataLink *)link
        !           245: {
        !           246:     return link;
        !           247: }
        !           248: 
        !           249: /* Form entry methods. */
        !           250: 
        !           251: /*
        !           252:  * Form Entries are essentially text items whose location, font, etc., are
        !           253:  * written out separately in an ASCII file when a Draw document is saved.
        !           254:  * When this is done, an EPS image of the Draw view is also written out
        !           255:  * (both of these files are place along with the document in the file package).
        !           256:  * These ASCII descriptions can then be used by other applications to overlay
        !           257:  * fields on top of a background of what is created by Draw.
        !           258:  *
        !           259:  * The most notable client of this right now is the Fax stuff.
        !           260:  */
        !           261: 
        !           262: - initFormEntry:(const char *)entryName localizable:(BOOL)isLocalizable
        !           263: /*
        !           264:  * The localizeFormEntry stuff is used by the Fax stuff in the following manner:
        !           265:  * If a form entry is localizable, then it appears in Draw in whatever the local
        !           266:  * language is, but, when written to the ASCII form.info file, it is written out
        !           267:  * not-localized.  Then, when the entity that reads the form.info file reads it,
        !           268:  * it is responsible for localizing it.  This enables the entity reading the
        !           269:  * form to actually semantically understand what a given form entry is (e.g. it
        !           270:  * is the To: field in a Fax Cover Sheet).
        !           271:  */ 
        !           272: {
        !           273:     char *buffer;
        !           274:     int maxlen;
        !           275:     NXStream *stream;
        !           276: 
        !           277:     [self init];
        !           278:     gFlags.isFormEntry = YES;
        !           279:     gFlags.localizeFormEntry = isLocalizable ? YES : NO;
        !           280:     bounds.size.width = 300.0;
        !           281:     bounds.size.height = 30.0;
        !           282:     [drawText setText:entryName];
        !           283:     [drawText setSel:0:100000];
        !           284:     [drawText setSelColor:NX_COLORBLACK];
        !           285:     [drawText setFont:[Font userFontOfSize:24.0 matrix:NX_FLIPPEDMATRIX]];
        !           286:     [drawText setHorizResizable:YES];
        !           287:     [drawText setVertResizable:YES];
        !           288:     bounds.size.width = bounds.size.height = 10000.0;
        !           289:     [drawText setMaxSize:&bounds.size];
        !           290:     [drawText calcLine];
        !           291:     [drawText getMinWidth:&bounds.size.width minHeight:&bounds.size.height maxWidth:10000.0 maxHeight:10000.0];
        !           292:     bounds.origin.x = bounds.origin.y = 0.0;
        !           293:     bounds.size.width = 300.0;
        !           294:     stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
        !           295:     [drawText writeRichText:stream];
        !           296:     NXGetMemoryBuffer(stream, &buffer, &length, &maxlen);
        !           297:     NX_ZONEMALLOC([self zone], data, char, length);
        !           298:     bcopy(buffer, data, length);
        !           299:     NXCloseMemory(stream, NX_FREEBUFFER);
        !           300:     
        !           301:     return [self finishedWithInit];
        !           302: }
        !           303: 
        !           304: #define LOCAL_FORM_ENTRY(s) \
        !           305:     NXLoadLocalStringFromTableInBundle("CoverSheet", [NXBundle mainBundle], s, NULL)
        !           306: #define FORM_ENTRY_BUF_SIZE 100
        !           307: 
        !           308: - prepareFormEntry
        !           309: /*
        !           310:  * Loads up the drawText with all the right attributes to
        !           311:  * display a form entry.  Called from draw.
        !           312:  */
        !           313: {
        !           314:     NXCoord width, height;
        !           315:     char *s, buffer[FORM_ENTRY_BUF_SIZE];
        !           316: 
        !           317:     [drawText setTextGray:NX_LTGRAY];
        !           318:     [drawText setFont:[drawText font]];
        !           319:     [drawText setAlignment:NX_LEFTALIGNED];
        !           320:     [drawText getSubstring:buffer start:0 length:FORM_ENTRY_BUF_SIZE];
        !           321:     buffer[FORM_ENTRY_BUF_SIZE-1] = '\0';
        !           322:     if ((s = strchr(buffer, '\n')) || gFlags.localizeFormEntry) {
        !           323:        if (s) *s = '\0';
        !           324:        if (gFlags.localizeFormEntry) {
        !           325:            [drawText setText:LOCAL_FORM_ENTRY(buffer)];
        !           326:        } else {
        !           327:            [drawText setText:buffer];
        !           328:        }
        !           329:     }
        !           330:     [drawText setHorizResizable:YES];
        !           331:     [drawText setVertResizable:YES];
        !           332:     [drawText setMaxSize:&bounds.size];
        !           333:     [drawText calcLine];
        !           334:     [drawText getMinWidth:&width minHeight:&height maxWidth:10000.0 maxHeight:10000.0];
        !           335:     if (width > bounds.size.width) width = bounds.size.width;
        !           336:     if (height > bounds.size.height) height = bounds.size.height;
        !           337:     [drawText sizeTo:width :height];
        !           338:     [drawText moveTo:bounds.origin.x + floor((bounds.size.width - width) / 2.0)
        !           339:                    :bounds.origin.y + floor((bounds.size.height - height) / 2.0)];
        !           340: 
        !           341:     return self;
        !           342: }
        !           343: 
        !           344: - (BOOL)isFormEntry
        !           345: {
        !           346:     return gFlags.isFormEntry;
        !           347: }
        !           348: 
        !           349: - setFormEntry:(int)flag
        !           350: {
        !           351:     gFlags.isFormEntry = flag ? YES : NO;
        !           352:     return self;
        !           353: }
        !           354: 
        !           355: - (Font *)getFormEntry:(char *)buffer andGray:(float *)gray
        !           356: /*
        !           357:  * Gets the information which will be written out into the
        !           358:  * form.info ASCII form entry description file.  Specifically,
        !           359:  * it gets the gray value, the actually name of the entry, and
        !           360:  * the Font of the entry.
        !           361:  */
        !           362: {
        !           363:     char *s;
        !           364:     NXStream *stream;
        !           365: 
        !           366:     if (gFlags.isFormEntry) {
        !           367:        stream = NXOpenMemory(data, length, NX_READONLY);
        !           368:        [drawText readRichText:stream];
        !           369:        [drawText setSel:0 :0];
        !           370:        if (gray) *gray = [drawText selGray];
        !           371:        NXCloseMemory(stream, NX_SAVEBUFFER);
        !           372:        [drawText getSubstring:buffer start:0 length:FORM_ENTRY_BUF_SIZE];
        !           373:        buffer[FORM_ENTRY_BUF_SIZE-1] = '\0';
        !           374:        if (s = strchr(buffer, '\n')) *s = '\0';
        !           375:        return [drawText font];
        !           376:     }
        !           377: 
        !           378:     return nil;
        !           379: }
        !           380: 
        !           381: - (BOOL)writeFormEntryToStream:(NXStream *)stream
        !           382: /*
        !           383:  * Writes out the ASCII representation of the location, gray,
        !           384:  * etc., of this form entry.  This is called only during
        !           385:  * the saving of a Draw document.
        !           386:  */
        !           387: {
        !           388:     Font *myFont;
        !           389:     float gray;
        !           390:     char buffer[FORM_ENTRY_BUF_SIZE];
        !           391: 
        !           392:     if (myFont = [self getFormEntry:buffer andGray:&gray]) {
        !           393:        NXPrintf(stream, "Entry: %s\n", buffer);
        !           394:        NXPrintf(stream, "Font: %s\n", [myFont name]);
        !           395:        NXPrintf(stream, "Font Size: %f\n", [myFont pointSize]);
        !           396:        NXPrintf(stream, "Text Gray: %f\n", gray);
        !           397:        NXPrintf(stream, "Location: x = %d, y = %d, w = %d, h = %d\n",
        !           398:            (int)bounds.origin.x, (int)bounds.origin.y,
        !           399:            (int)bounds.size.width, (int)bounds.size.height);
        !           400:        return YES;
        !           401:     }
        !           402: 
        !           403:     return NO;
        !           404: }
        !           405: 
        !           406: /* Factory methods overridden from superclass */
        !           407: 
        !           408: + (BOOL)isEditable
        !           409: {
        !           410:     return YES;
        !           411: }
        !           412: 
        !           413: + cursor
        !           414: {
        !           415:     return NXIBeam;
        !           416: }
        !           417: 
        !           418: /* Instance methods overridden from superclass */
        !           419: 
        !           420: - (const char *)title
        !           421: {
        !           422:     return NXLocalStringFromTable("Operations", "Text", NULL, "The %s of the `New %s' operation corresponding to creating an area for the user to type into.");
        !           423: }
        !           424: 
        !           425: - (BOOL)create:(NXEvent *)event in:(GraphicView *)view
        !           426:  /*
        !           427:   * We are only interested in where the mouse goes up, that's
        !           428:   * where we'll start editing.
        !           429:   */
        !           430: {
        !           431:     NXRect viewBounds;
        !           432: 
        !           433:     event = [NXApp getNextEvent:NX_MOUSEUPMASK];
        !           434:     bounds.size.width = bounds.size.height = 0.0;
        !           435:     bounds.origin = event->location;
        !           436:     [view convertPoint:&bounds.origin fromView:nil];
        !           437:     [view getBounds:&viewBounds];
        !           438:     gFlags.selected = NO;
        !           439: 
        !           440:     return NXMouseInRect(&bounds.origin, &viewBounds, NO);
        !           441: }
        !           442: 
        !           443: - (BOOL)edit:(NXEvent *)event in:(View *)view
        !           444: {
        !           445:     id change;
        !           446:     NXRect eb;
        !           447: 
        !           448:     if (gFlags.isFormEntry && gFlags.localizeFormEntry) return NO;
        !           449:     if ([self link]) return NO;
        !           450: 
        !           451:     editView = view;
        !           452:     graphicView = [editView superview];
        !           453:     
        !           454:     /* Get the field editor in this window. */
        !           455: 
        !           456:     if (gFlags.isFormEntry) {
        !           457:        gFlags.isFormEntry = NO;
        !           458:        [[view superview] cache:[self getExtendedBounds:&eb]];  // gFlags.isFormEntry starts editing
        !           459:        [[view window] flushWindow];
        !           460:        gFlags.isFormEntry = YES;
        !           461:     }
        !           462: 
        !           463:     change = [[StartEditingGraphicsChange alloc] initGraphic:self];
        !           464:     [change startChange];
        !           465:        [self prepareFieldEditor];
        !           466:        if (event) {  
        !           467:            [fe selectNull];    /* eliminates any existing selection */
        !           468:            [fe mouseDown:event]; /* Pass the event on to the Text object */
        !           469:        } 
        !           470:     [change endChange];
        !           471: 
        !           472:     return YES;
        !           473: }
        !           474: 
        !           475: - draw
        !           476:  /*
        !           477:   * If the region has already been created, then we must draw the text.
        !           478:   * To do this, we first load up the shared drawText Text object with
        !           479:   * our rich text.  We then set the frame of the drawText object
        !           480:   * to be our bounds.  Finally, we add the Text object as a subview of
        !           481:   * the view that is currently being drawn in ([NXApp focusView])
        !           482:   * and tell the Text object to draw itself.  We then remove the Text
        !           483:   * object view from the view heirarchy.
        !           484:   */
        !           485: {
        !           486:     NXStream *stream;
        !           487: 
        !           488:     if (data && (!gFlags.isFormEntry || NXDrawingStatus == NX_DRAWING)) {
        !           489:        stream = NXOpenMemory(data, length, NX_READONLY);
        !           490:        [drawText readRichText:stream];
        !           491:        NXCloseMemory(stream, NX_SAVEBUFFER);
        !           492:        if (gFlags.isFormEntry) {
        !           493:            [self prepareFormEntry];
        !           494:        } else {
        !           495:            [drawText setFrame:&bounds];
        !           496:        }
        !           497:        [[NXApp focusView] addSubview:drawText];
        !           498:        [drawText display];
        !           499:        [drawText removeFromSuperview];
        !           500:        if (DrawStatus == Resizing || gFlags.isFormEntry) {
        !           501:            PSsetgray(NX_LTGRAY);
        !           502:            NXFrameRect(&bounds);
        !           503:        }
        !           504:     }
        !           505: 
        !           506:     return self;
        !           507: }
        !           508: 
        !           509: - performTextMethod:(SEL)aSelector with:(void *)anArgument
        !           510: /*
        !           511:  * This performs the given aSelector on the text by loading up
        !           512:  * a Text object and applying aSelector to it (with selectAll:
        !           513:  * having been done first).  See PerformTextGraphicsChange.m
        !           514:  * in graphicsUndo.subproj.
        !           515:  */
        !           516: {
        !           517:     id change;
        !           518: 
        !           519:     if (data) {
        !           520:        change = [PerformTextGraphicsChange alloc];
        !           521:        [change initGraphic:self view:graphicView];
        !           522:        [change startChangeIn:graphicView];
        !           523:            [change loadGraphic];
        !           524:            [[change editText] perform:aSelector with:anArgument];
        !           525:            [change unloadGraphic];
        !           526:        [change endChange];
        !           527:     }
        !           528: 
        !           529:     return self;
        !           530: }
        !           531: 
        !           532: - setFont:aFont
        !           533: {
        !           534:     font = aFont;
        !           535:     return self;
        !           536: }
        !           537: 
        !           538: - (char *)data
        !           539: {
        !           540:     return data;
        !           541: }
        !           542: 
        !           543: - setData:(char *)newData
        !           544: {
        !           545:     if (data) NX_FREE(data);
        !           546:     data = newData;
        !           547:     return self;
        !           548: }
        !           549: 
        !           550: - (int)length
        !           551: {
        !           552:     return length;
        !           553: }
        !           554: 
        !           555: - setLength:(int)newLength
        !           556: {
        !           557:     length = newLength;
        !           558:     return self;
        !           559: }
        !           560: 
        !           561: - changeFont:sender
        !           562: {
        !           563:     [self performTextMethod:@selector(changeFont:) with:sender];
        !           564:     return self;
        !           565: }
        !           566: 
        !           567: - (Font *)font
        !           568: {
        !           569:     NXStream *stream;
        !           570: 
        !           571:     if (!font && data) {
        !           572:        stream = NXOpenMemory(data, length, NX_READONLY);
        !           573:        [drawText readRichText:stream];
        !           574:        NXCloseMemory(stream, NX_SAVEBUFFER);
        !           575:        [drawText setSel:0 :0];
        !           576:        font = [drawText font];
        !           577:     }
        !           578: 
        !           579:     return font;
        !           580: }
        !           581: 
        !           582: - (BOOL)isOpaque
        !           583: /*
        !           584:  * We are never opaque.
        !           585:  */
        !           586: {
        !           587:     return NO;
        !           588: }
        !           589: 
        !           590: - (BOOL)isValid
        !           591: /*
        !           592:  * Any size TextGraphic is valid (since we fix up the size if it is
        !           593:  * too small in our override of create:in:).
        !           594:  */
        !           595: {
        !           596:     return YES;
        !           597: }
        !           598: 
        !           599: - (NXColor)lineColor
        !           600: {
        !           601:     return NX_COLORBLACK;
        !           602: }
        !           603: 
        !           604: - (NXColor)fillColor
        !           605: {
        !           606:     return NX_COLORWHITE;
        !           607: }
        !           608: 
        !           609: - (NXCoord)baseline
        !           610: {
        !           611:     NXCoord ascender, descender, lineHeight;
        !           612: 
        !           613:     if (!font) [self font];
        !           614:     if (font) {
        !           615:        NXTextFontInfo(font, &ascender, &descender, &lineHeight);
        !           616:        return bounds.origin.y + bounds.size.height + ascender;
        !           617:     }
        !           618: 
        !           619:     return 0;
        !           620: }
        !           621: 
        !           622: - moveBaselineTo:(NXCoord *)y
        !           623: {
        !           624:     NXCoord ascender, descender, lineHeight;
        !           625: 
        !           626:     if (y && !font) [self font];
        !           627:     if (y && font) {
        !           628:        NXTextFontInfo(font, &ascender, &descender, &lineHeight);
        !           629:        bounds.origin.y = *y - ascender - bounds.size.height;
        !           630:     }
        !           631: 
        !           632:     return self;
        !           633: }
        !           634: 
        !           635: /* Public methods */
        !           636: 
        !           637: - prepareFieldEditor
        !           638: /*
        !           639:  * Here we are going to use the shared field editor for the window to
        !           640:  * edit the text in the TextGraphic.  First, we must end any other editing
        !           641:  * that is going on with the field editor in this window using endEditingFor:.
        !           642:  * Next, we get the field editor from the window.  Normally, the field
        !           643:  * editor ends editing when carriage return is pressed.  This is due to
        !           644:  * the fact that its character filter is NXFieldFilter.  Since we want our
        !           645:  * editing to be more like an editor (and less like a Form or TextField),
        !           646:  * we set the character filter to be NXEditorFilter.  What is more, normally,
        !           647:  * you can't change the font of a TextField or Form with the FontPanel
        !           648:  * (since that might interfere with any real editable Text objects), but
        !           649:  * in our case, we do want to be able to do that.  We also want to be
        !           650:  * able to edit rich text, so we issue a setMonoFont:NO.  Editing is a bit
        !           651:  * more efficient if we set the Text object to be opaque.  Note that
        !           652:  * in textDidEnd:endChar: we will have to set the character filter,
        !           653:  * FontPanelEnabled and mono-font back so that if there were any forms
        !           654:  * or TextFields in the window, they would have a correctly configured
        !           655:  * field editor.
        !           656:  *
        !           657:  * To let the field editor know exactly where editing is occurring and how
        !           658:  * large the editable area may grow to, we must calculate and set the frame
        !           659:  * of the field editor as well as its minimum and maximum size.
        !           660:  *
        !           661:  * We load up the field editor with our rich text (if any).
        !           662:  *
        !           663:  * Finally, we set self as the delegate (so that it will receive the
        !           664:  * textDidEnd:endChar: message when editing is completed) and either
        !           665:  * pass the mouse-down event onto the Text object, or, if a mouse-down
        !           666:  * didn't cause editing to occur (i.e. we just created it), then we
        !           667:  * simply put the blinking caret at the beginning of the editable area.
        !           668:  *
        !           669:  * The line marked with the "ack!" is kind of strange, but is necessary
        !           670:  * since growable Text objects only work when they are subviews of a flipped
        !           671:  * view.
        !           672:  *
        !           673:  * This is why GraphicView has an "editView" which is a flipped view that it
        !           674:  * inserts as a subview of itself for the purposes of providing a superview
        !           675:  * for the Text object.  The "ack!" line converts the bounds of the TextGraphic
        !           676:  * (which are in GraphicView coordinates) to the coordinates of the Text
        !           677:  * object's superview (the editView).  This limitation of the Text object
        !           678:  * will be fixed post-1.0.  Note that the "ack!" line is the only one
        !           679:  * concession we need to make to this limitation in this method (there is
        !           680:  * another such line in resignFieldEditor).
        !           681:  */
        !           682: {
        !           683:     NXSize maxSize;
        !           684:     NXStream *stream;
        !           685:     NXRect viewBounds, frame, eb;
        !           686: 
        !           687:     [NXApp sendAction:@selector(disableChanges:) to:nil from:self];
        !           688:        [[graphicView window] endEditingFor:self];
        !           689:        fe = [[graphicView window] getFieldEditor:YES for:self];
        !           690:        
        !           691:        if ([self isSelected]) {
        !           692:            [self deselect];
        !           693:            [graphicView cache:[self getExtendedBounds:&eb] andUpdateLinks:NO];
        !           694:            [[graphicView selectedGraphics] removeObject:self];
        !           695:        }
        !           696:        
        !           697:        [fe setFont:[[FontManager new] selFont]];
        !           698:     
        !           699:        /* Modify it so that it will edit Rich Text and use the FontPanel. */
        !           700:     
        !           701:        [fe setCharFilter:NXEditorFilter];
        !           702:        [fe setFontPanelEnabled:YES];
        !           703:        [fe setMonoFont:NO];
        !           704:        [fe setOpaque:YES];
        !           705:     
        !           706:        /*
        !           707:            * Determine the minimum and maximum size that the Text object can be.
        !           708:            * We let the Text object grow out to the edges of the GraphicView,
        !           709:            * but no further.
        !           710:            */
        !           711:     
        !           712:        [editView getBounds:&viewBounds];
        !           713:        maxSize.width = viewBounds.origin.x+viewBounds.size.width-bounds.origin.x;
        !           714:        maxSize.height = bounds.origin.y+bounds.size.height-viewBounds.origin.y;
        !           715:        if (!bounds.size.height && !bounds.size.width) {
        !           716:            bounds.origin.y -= floor([fe lineHeight] / 2.0);
        !           717:            bounds.size.height = [fe lineHeight];
        !           718:            bounds.size.width = 5.0;
        !           719:        }
        !           720:        frame = bounds;
        !           721:        [editView convertRect:&frame fromView:graphicView];     // ack!
        !           722:        [fe setMinSize:&bounds.size];
        !           723:        [fe setMaxSize:&maxSize];
        !           724:        [fe setFrame:&frame];
        !           725:        [fe setVertResizable:YES];
        !           726:     
        !           727:        /*
        !           728:            * If we already have text, then put it in the Text object (allowing
        !           729:            * the Text object to grow downward if necessary), otherwise, put
        !           730:            * no text in, set some initial parameters, and allow the Text object
        !           731:            * to grow horizontally as well as vertically
        !           732:            */
        !           733:     
        !           734:        if (data) {
        !           735:            [fe setHorizResizable:NO];
        !           736:            stream = NXOpenMemory(data, length, NX_READONLY);
        !           737:            [fe readRichText:stream];
        !           738:            NXCloseMemory(stream, NX_SAVEBUFFER);
        !           739:        } else {
        !           740:            [fe setHorizResizable:YES];
        !           741:            [fe setText:""];
        !           742:            [fe setAlignment:NX_LEFTALIGNED];
        !           743:            [fe setSelColor:NX_COLORBLACK];
        !           744:            [fe unscript:self];
        !           745:        }
        !           746:     
        !           747:        /*
        !           748:            * Add the Text object to the view heirarchy and set self as its delegate
        !           749:            * so that we will receive the textDidEnd:endChar: message when editing
        !           750:            * is finished.
        !           751:            */
        !           752:     
        !           753:        [fe setDelegate:self];
        !           754:        [editView addSubview:fe];
        !           755:     
        !           756:        /*
        !           757:            * Make it the first responder.
        !           758:            */
        !           759:     
        !           760:        [[graphicView window] makeFirstResponder:fe];
        !           761:     
        !           762:        /* Change the ruler to be a text ruler. */
        !           763:     
        !           764:        [fe tryToPerform:@selector(showTextRuler:) with:fe];
        !           765:     
        !           766:        [fe setSel:0:0];
        !           767:     [NXApp sendAction:@selector(enableChanges:) to:nil from:self];
        !           768: 
        !           769:     return self;
        !           770: }
        !           771: 
        !           772: - resignFieldEditor
        !           773: /* 
        !           774:  * We must extract the rich text the user has typed from the Text object,
        !           775:  * and store it away. We also need to get the frame of the Text object
        !           776:  * and make that our bounds (but, remember, since the Text object must
        !           777:  * be a subview of a flipped view, we need to convert the bounds rectangle
        !           778:  * to the coordinates of the unflipped GraphicView).  If the Text object
        !           779:  * is empty, then we remove this TextGraphic from the GraphicView.
        !           780:  * We must remove the Text object from the view heirarchy and, since
        !           781:  * this Text object is going to be reused, we must set its delegate
        !           782:  * back to nil.
        !           783:  *
        !           784:  * For further explanation of the "ack!" line, see edit:in: above.
        !           785:  */
        !           786: {
        !           787:     int maxlen;
        !           788:     char *buffer;
        !           789:     NXStream *stream;
        !           790:     NXRect oldBounds, *redrawRect = NULL;
        !           791: 
        !           792:     [NXApp sendAction:@selector(disableChanges:) to:nil from:self];
        !           793:        if (data) {
        !           794:            NX_FREE(data);
        !           795:            data = NULL;
        !           796:            length = 0;
        !           797:        }
        !           798:     
        !           799:        NX_ASSERT(editView == [fe superview], "Fault in Text Graphic: Code 2");
        !           800:        NX_ASSERT(graphicView == [editView superview], "Fault in Text Graphic: Code 3");
        !           801:     
        !           802:        if ([fe textLength]) {
        !           803:            stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
        !           804:            [fe writeRichText:stream];
        !           805:            NXGetMemoryBuffer(stream, &buffer, &length, &maxlen);
        !           806:            NX_ZONEMALLOC([self zone], data, char, length);
        !           807:            bcopy(buffer, data, length);
        !           808:            NXCloseMemory(stream, NX_FREEBUFFER);
        !           809:            oldBounds = bounds;
        !           810:            [fe getFrame:&bounds];
        !           811:            [editView convertRect:&bounds toView:graphicView];  // ack!
        !           812:            NXUnionRect(&bounds, &oldBounds);
        !           813:            redrawRect = &oldBounds;
        !           814:        }
        !           815: 
        !           816:        if (redrawRect) [[graphicView window] disableFlushWindow];
        !           817: 
        !           818:        [graphicView tryToPerform:@selector(hideRuler:) with:nil];
        !           819:        [fe removeFromSuperview];
        !           820:        [fe setDelegate:nil];
        !           821:        [fe setSel:0 :0];
        !           822:        font = [fe font];
        !           823:     
        !           824:        if (redrawRect) {
        !           825:            [graphicView cache:redrawRect];
        !           826:            [[graphicView window] reenableFlushWindow];
        !           827:            [[graphicView window] flushWindow];
        !           828:        }
        !           829: 
        !           830:        fe = nil;
        !           831:     [NXApp sendAction:@selector(enableChanges:) to:nil from:self];
        !           832:     
        !           833:     return self;
        !           834: }
        !           835: 
        !           836: - (BOOL)isEmpty
        !           837: {
        !           838:     return data ? NO : YES;
        !           839: }
        !           840: 
        !           841: /* Text object delegate methods */
        !           842: 
        !           843: /*
        !           844:  * If we have more than one line, turn off horizontal resizing.
        !           845:  */
        !           846: - textDidResize:textObject oldBounds:(const NXRect *)oldBounds invalid:(NXRect *)invalidRect
        !           847: {
        !           848:     NXSelPt start,end;
        !           849: 
        !           850:     [textObject getSel:&start :&end];
        !           851:     if (start.line || end.line)
        !           852:       [textObject setHorizResizable:NO];
        !           853:     return self;
        !           854: }
        !           855: 
        !           856: - textDidEnd:textObject endChar:(unsigned short)endChar
        !           857: /*
        !           858:  * This method is called when ever first responder is taken away from a
        !           859:  * currently editing TextGraphic (i.e. when the user is done editing and
        !           860:  * chooses to go do something else).  
        !           861:  */
        !           862: {
        !           863:     id change;
        !           864: 
        !           865:     NX_ASSERT(fe == textObject, "Fault in Text Graphic: Code 1")
        !           866:     
        !           867:     change = [[EndEditingGraphicsChange alloc] initGraphicView:graphicView  graphic:self];
        !           868:     [change startChange];
        !           869:         [self resignFieldEditor];
        !           870:        if ([self isEmpty])
        !           871:            [graphicView removeGraphic:self];
        !           872:     [change endChange];
        !           873: 
        !           874:     return self;
        !           875: }
        !           876: 
        !           877: /* Archiving methods */
        !           878: 
        !           879: - awake
        !           880: {
        !           881:     initClassVars();
        !           882:     return [super awake];
        !           883: }
        !           884: 
        !           885: - write:(NXTypedStream *)stream
        !           886:  /*
        !           887:   * Writes the TextGraphic out to the typed stream.
        !           888:   */
        !           889: {
        !           890:     [super write:stream];
        !           891:     NXWriteTypes(stream, "i", &length);
        !           892:     NXWriteArray(stream, "c", length, data);
        !           893:     return self;
        !           894: }
        !           895: 
        !           896: - read:(NXTypedStream *)stream
        !           897:  /*
        !           898:   * Reads the TextGraphic in from the typed stream.
        !           899:   * This is versioned.  The old way we used to implement
        !           900:   * this class included using a Cell object.  Now we
        !           901:   * use the Text object directly.
        !           902:   */
        !           903: {
        !           904:     int version;
        !           905: 
        !           906:     version = NXTypedStreamClassVersion(stream, "TextGraphic");
        !           907:     [super read:stream];
        !           908: 
        !           909:     if (version < 1) {
        !           910:        Cell *cell;
        !           911:        int maxlen;
        !           912:        NXStream *s;
        !           913:        char *buffer;
        !           914:        NXReadTypes(stream, "@", &cell);
        !           915:        [drawText setText:[cell stringValue]];
        !           916:        font = [cell font];
        !           917:        [drawText setFont:[cell font]];
        !           918:        [drawText setTextColor:[self lineColor]];
        !           919:        s = NXOpenMemory(NULL, 0, NX_WRITEONLY);
        !           920:        [drawText writeRichText:s];
        !           921:        NXGetMemoryBuffer(s, &buffer, &length, &maxlen);
        !           922:        NX_ZONEMALLOC([self zone], data, char, length);
        !           923:        bcopy(buffer, data, length);
        !           924:        NXCloseMemory(s, NX_FREEBUFFER);
        !           925:     } else {
        !           926:        NXReadTypes(stream, "i", &length);
        !           927:        NX_ZONEMALLOC([self zone], data, char, length);
        !           928:        NXReadArray(stream, "c", length, data);
        !           929:     }
        !           930: 
        !           931:     if (version > 2 && version < 5) {
        !           932:        int linkNumber;
        !           933:        NXReadTypes(stream, "i", &linkNumber);
        !           934:     } else if (version == 2) {
        !           935:        int linkNumber;
        !           936:        link = NXReadObject(stream);
        !           937:        linkNumber = [link linkNumber];
        !           938:        link = nil;
        !           939:     }
        !           940: 
        !           941:     if (version > 3 && version < 6) {
        !           942:        BOOL isFormEntry;
        !           943:        NXReadTypes(stream, "c", &isFormEntry);
        !           944:        gFlags.isFormEntry = isFormEntry ? YES : NO;
        !           945:     }
        !           946: 
        !           947:     return self;
        !           948: }
        !           949: 
        !           950: @end

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.