Annotation of Examples/AppKit/Draw/TextGraphic.m, revision 1.1.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.