This is DrawingView.m in view mode; [Download] [Up]
/*
* (a) (C) 1990 by Adobe Systems Incorporated. All rights reserved.
*
* (b) If this Sample Code is distributed as part of the Display PostScript
* System Software Development Kit from Adobe Systems Incorporated,
* then this copy is designated as Development Software and its use is
* subject to the terms of the License Agreement attached to such Kit.
*
* (c) If this Sample Code is distributed independently, then the following
* terms apply:
*
* (d) This file may be freely copied and redistributed as long as:
* 1) Parts (a), (d), (e) and (f) continue to be included in the file,
* 2) If the file has been modified in any way, a notice of such
* modification is conspicuously indicated.
*
* (e) PostScript, Display PostScript, and Adobe are registered trademarks of
* Adobe Systems Incorporated.
*
* (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
* CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
* AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
* ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
* OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
* WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
* WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
* DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
* OF THIRD PARTY RIGHTS.
*/
/*
* DrawingView.m
*
* This view represents the page that the image is drawn onto. It is
* a subview of the DocView. The DocView is the document view of
* the ClipView. This view's real size grows with the scale but the
* bounds always stays the same.
*
* Version: 2.0
* Author: Ken Fromm
* History:
* 03-07-91 Added this comment.
*/
#import "DrawingView.h"
#import "DrawingViewWraps.h"
#import "DocView.h"
#import "TextApp.h"
#import "hdshowaux.h"
#import <appkit/Button.h>
#import <appkit/Cell.h>
#import <appkit/Font.h>
#import <appkit/Matrix.h>
#import <appkit/nextstd.h>
#import <dpsclient/dpsclient.h>
#import <dpsclient/wraps.h>
#import <ctype.h>
extern char*const textstrings[];
extern float textstart[][2];
@implementation DrawingView
/*
* Allocate a gstate, set the clipping to NO because it will be clipped
* by the clip view.
*/
+newFrame:(NXRect *) frm
{
id font;
self = [super newFrame:frm];
[[[self allocateGState] setClipping:NO] setFlipped:YES];
NX_MALLOC(charspace, NXCoord, MAX_XSHOW);
AllocShowStruct(&s);
showtype = XSHOW; /* use xshow */
cache = YES; /* use font cache */
fontsize = FONTSIZE;
font = [Font newFont:"Times-Roman" size:fontsize];
PSWCopyFont("Times-Roman", "Times-RomanOutline");
PSWCurrentcacheparams(&size, &lower, &upper);
return self;
}
- free
{
PSWSetcacheparams(size, lower, upper);
NXPing();
NX_FREE(charspace);
FreeShowStruct(&s);
return [super free];
}
- (int) rotation
{
return rotation;
}
- fontsize:sender
{
fontsize = atof([[sender selectedCell] title]);
[self reshow:self];
return self;
}
- justify:sender
{
justify = ([sender selectedTag] != 0);
[self reshow:self];
return self;
}
- kern:sender
{
kern = [[sender selectedCell] state];
[self reshow:self];
return self;
}
- track:sender
{
track = [[sender selectedCell] state];
[self reshow:self];
return self;
}
/*
* Sets the font cache to an arbitrarily low size. It's almost never necessary
* to change the font cache size. This is only here to provide some
* demonstration of the effectiveness of the font cache. Its operation is
* best left alone.
*/
- cache:sender
{
cache = [[sender selectedCell] state];
if (cache)
PSWSetcacheparams(size, lower, upper);
else
PSWSetcacheparams(0, 0, 0);
[self reshow:self];
return self;
}
- outline:sender
{
outline = [[sender selectedCell] state];
[self reshow:self];
return self;
}
- show:sender
{
showtype = [sender selectedTag];
[self reshow:self];
return self;
}
- compareKerns:sender
{
id cell;
compareKern = [[sender selectedCell] state];
if (compareKern && compareWidths)
{
compareWidths = NO;
cell = [[NXApp comparisonsMatrix] findCellWithTag:WIDTH_TAG];
[cell setState:NO];
[[cell controlView] drawCell:cell];
}
[self reshow:self];
return self;
}
- compareWidths:sender
{
id cell;
compareWidths = [[sender selectedCell] state];
if (compareWidths && compareKern)
{
compareKern = NO;
cell = [[NXApp comparisonsMatrix] findCellWithTag:KERN_TAG];
[cell setState:NO];
[[cell controlView] drawCell:cell];
}
[self reshow:self];
return self;
}
- rotate:sender
{
id field;
field = [NXApp rotationField];
if (sender == field)
rotation = atoi([field stringValue]);
else if ([sender selectedTag] == 0)
rotation += ROTATION;
else
rotation -= ROTATION;
rotation = rotation % 360;
[field setIntValue:rotation];
[self resetFields:self];
[self rotateTo:(float) rotation];
[superview rotateDrawView];
[self displayFields:self];
return self;
}
/*
* Changes the title of the menu cell according
* to the value of the trace variable.
*/
-trace:sender
{
if (trace == NO)
[[sender selectedCell] setTitle:"Trace On"];
else
[[sender selectedCell] setTitle:"Trace Off"];
trace = !trace;
return self;
}
- eraseFields:sender
{
id matrixId;
int i;
matrixId = [NXApp timingMatrix];
for (i= 0; i < [matrixId cellCount]; i++)
[[matrixId cellAt:i :0] setStringValue:""];
matrixId = [NXApp statusMatrix];
for (i= 0; i < [matrixId cellCount]; i++)
[[matrixId cellAt:i :0] setStringValue:""];
eraseFields = NO;
return self;
}
- resetFields:sender
{
/* Prepare for next time around. */
timing_info.chars = timing_info.reshows = timing_info.kerns = timing_info.time = 0;
return self;
}
- displayFields:sender
{
id matrixId;
int i, cvalues[7];
if (timing_info.reshows)
{
matrixId = [NXApp timingMatrix];
[[matrixId cellAt:0 :0] setIntValue:rint(timing_info.time/timing_info.reshows)];
[[matrixId cellAt:1 :0] setIntValue:rint(timing_info.chars/timing_info.reshows)];
[[matrixId cellAt:2 :0] setIntValue:rint(timing_info.kerns/timing_info.reshows)];
matrixId = [NXApp statusMatrix];
PSWCachestatus(cvalues);
for (i= 0; i < [matrixId cellCount]; i++)
[[matrixId cellAt:i :0] setIntValue:cvalues[i]];
}
[self resetFields:self];
eraseFields = YES;
return self;
}
- reshow:sender
{
NXRect visRect;
eraseFields = NO;
[self resetFields:self];
[self getVisibleRect:&visRect];
[self display:&visRect :1];
[self displayFields:self];
return self;
}
/*
* If the docview is zooming, then scale the drawing view.
*/
- mouseDown:(NXEvent *)event
{
NXPoint p;
p = event->location;
if ([superview isZooming])
{
eraseFields = NO;
[self resetFields:self];
[superview scaleDrawViewToPoint:&p];
[self displayFields:self];
}
return self;
}
static NXCoord filltrackwidths(char *str, float *array, ShowStruct *s, float value)
{
int i, len;
NXCoord trackwidth = 0.0;
if (str && value)
{
len = strlen(str);
if (array)
{
for (i = 0; i < len; i++)
array[i] += value;
}
else if (s)
AddTracking(s, 0, value);
trackwidth = len * value;
}
return trackwidth;
}
static void fillspacewidths(char *str, float *array, ShowStruct *s, float value)
{
int i, len;
if (str && value)
{
if (array)
{
len = strlen(str);
for (i = 0; i < len; i++)
if (str[i] == ' ')
array[i] += value;
}
else if (s)
AddSpaceAdj(s, 0, value);
}
}
/*
* If an array is passed in then place the character width for each character
* in str in it (used when displaying with xshow). Also keep track of the total width
* and the number of spaces and return the value of the width divided by the
* spaces (used when displaying with full justification).
*/
static NXCoord fillstringwidths(char *str, NXFontMetrics *metrics, float *array, float fontsize, NXCoord kernwidth, NXCoord trackwidth)
{
BOOL nospacing = NO;
int i, len,
spaces;
NXCoord value,
linewidth;
spaces = 0;
linewidth = kernwidth + trackwidth;
if (str && metrics && metrics->widths)
{
len = strlen(str);
for (i = 0; i < len; i++)
{
value = metrics->widths[(unsigned char)str[i]] * fontsize;
linewidth += value;
if (array)
array[i] += value;
/* Check for space, return, linefeed, etc. */
if(isspace(str[i]))
{
if (str[i] == ' ')
spaces ++;
else
nospacing = YES;
}
}
}
if (spaces == 0)
value = 0.0;
else
{
if (trackwidth)
value = (LINE_LENGTH_TR - linewidth)/spaces;
else
value = (LINE_LENGTH - linewidth)/spaces;
if (nospacing && value > 0)
value = 0.0;
}
return value;
}
/*
* Look up the kern pairs for the first character and see if
* there is an entry for the second character. Returns the
* value to kern if any.
*/
static NXCoord getkernvalue(NXFontMetrics *metrics, unsigned char char1, unsigned char char2)
{
int enc1, enc2,
i, kindex, klen;
NXCoord value = 0.0;
enc1 = metrics->encoding[char1];
enc2 = metrics->encoding[char2];
if (enc1 < metrics->numCharMetrics && enc2 < metrics->numCharMetrics)
{
kindex = metrics->charMetrics[enc1].kernPairIndex;
klen = metrics->charMetrics[enc1].numKernPairs;
if (kindex+klen < metrics->numKernPairs)
{
for (i = kindex; i < kindex + klen; i++)
{
if (metrics->hasXYKerns)
{
if (enc2 == metrics->kerns.kernPairs[i].secondCharIndex)
{
value = metrics->kerns.kernPairs[i].dx;
break;
}
}
else
{
if (enc2 == metrics->kerns.kernXPairs[i].secondCharIndex)
{
value = metrics->kerns.kernXPairs[i].dx;
break;
}
}
}
}
}
return value;
}
static NXCoord fillkernwidths(char *str, NXFontMetrics *metrics, float *array, ShowStruct *s, float fontsize, int *kerns)
{
int i, len;
NXCoord value,
kernwidth;
kernwidth = 0.0;
if (str && metrics)
{
len = strlen(str) -1;
for (i = 0; i < len; i++)
{
if (value = getkernvalue(metrics, (unsigned char) str[i], (unsigned char) str[i+1]))
{
value = value * fontsize;
if (array)
array[i] += value;
else if (s)
AddPairKern(s, i+1, value);
kernwidth += value;
(*kerns)++;
}
}
}
return kernwidth;
}
/*
* Go through the line and insert the appropriate value into the array.
* The value placed in is dependent on whether we are kerning or not
* and whether we are using xshow or not. If kerning, then look up each
* pair in the AFM kerning array. If using xshow, then include the
* character width in the array as well (xshow takes the displacement from
* the previously shown character hence the character width).
* The fontsize is used to scale the kerning value and the character width.
* In a screenfont, no adjustment is necessary because the values are
* already scaled to the appropriate values.
*/
static int characterspacing(id font, char *str, NXCoord *array, BOOL justify, BOOL kern, BOOL track, BOOL width)
{
int kerns = 0;
float fontsize;
NXCoord kernwidth,
spacewidth,
trackwidth;
NXFontMetrics *metrics;
bzero(array, MAX_XSHOW*sizeof(NXCoord));
metrics = [font readMetrics:FONTMETRICS];
if (metrics)
{
if (metrics->isScreenFont)
fontsize = 1;
else
fontsize = [font pointSize];
kernwidth = spacewidth = trackwidth = 0.0;
if (kern && metrics->encoding && metrics->charMetrics)
kernwidth = fillkernwidths(str, metrics, array, NULL, fontsize, &kerns);
if (track)
trackwidth = filltrackwidths(str, array, NULL, TRACKVAL);
if (width && metrics->widths)
spacewidth = fillstringwidths(str, metrics, array, fontsize, kernwidth, trackwidth);
if (justify)
{
if (!width && metrics->widths)
spacewidth = fillstringwidths(str, metrics, NULL, fontsize,
kernwidth, trackwidth);
fillspacewidths(str, array, NULL, spacewidth);
}
}
return kerns;
}
static int showstructure(id font, char *str, ShowStruct *s, BOOL justify, BOOL kern, BOOL track)
{
int kerns = 0;
float fontsize;
NXCoord kernwidth,
spacewidth,
trackwidth;
NXFontMetrics *metrics;
metrics = [font readMetrics:FONTMETRICS];
if (metrics)
{
if (metrics->isScreenFont)
fontsize = 1;
else
fontsize = [font pointSize];
kernwidth = spacewidth = trackwidth = 0.0;
if (kern && metrics->encoding && metrics->charMetrics)
kernwidth = fillkernwidths(str, metrics, NULL, s, fontsize, &kerns);
if (track)
trackwidth = filltrackwidths(str, NULL, s, TRACKVAL);
if (justify)
{
spacewidth = fillstringwidths(str, metrics, NULL, fontsize, kernwidth, trackwidth);
fillspacewidths(str, NULL, s, spacewidth);
}
}
return kerns;
}
/*
* Set the font either to the printer font or the screenfont, turn the trace on
* if tracing and then mark the time. (The last two steps are for demonstration
* purposes only. Cycle through the static array of text checking if a line
* intersects the rectangle passed in. If so then draw it according to the values
* set in the interface - kerning or no kerning, screen widths or printer widths,
* xshow or rmoveto/show, etc.
*/
- showText:(const NXRect *)r
{
id font;
float x;
int i, j, last,
chars,
ElapsedTime;
NXRect textRect;
if (trace)
DPSTraceContext(DPSGetCurrentContext(), YES);
font = [Font newFont:"Times-Roman" size:fontsize];
if (outline)
PSWSetFont("Times-RomanOutline", fontsize);
else
[font set];
if (screen && [font screenFont])
font = [font screenFont];
textRect.origin.x = bounds.origin.x;
textRect.size.width = bounds.size.width;
textRect.size.height = 2 * fontsize;
PSsetwindowtype(NX_RETAINED, [window windowNum]);
PSWMarkTime(); NXPing();
for (i = 0; i < NUM_LINES; i++)
{
textRect.origin.y = textstart[i][1] - fontsize;
if (NXIntersectsRect(r, &textRect))
{
chars = strlen(textstrings[i]);
timing_info.chars += chars;
if (showtype == XSHOW || showtype == RMSHOW)
{
timing_info.kerns += characterspacing(font, textstrings[i], charspace, justify,
kern, track, (showtype == XSHOW));
if (showtype == XSHOW)
{
PSmoveto(textstart[i][0], textstart[i][1]);
PSxshow(textstrings[i], charspace, chars);
}
else
{
PSmoveto(textstart[i][0], textstart[i][1]);
for (j = 0, last = -1, x = 0.0; j < chars; j++)
{
if (charspace[j] != 0)
{
if (x == 0.0)
PSWShow(&textstrings[i][last+1], j - last);
else
PSWRmovetoShow(x, &textstrings[i][last+1], j - last);
last = j;
x = charspace[j];
}
}
if (x == 0.0)
{
if (j-last > 0)
PSWShow(&textstrings[i][last+1], j - last);
}
else
{
if (j-last-1 > 0)
PSWRmovetoShow(x, &textstrings[i][last+1], j - last -1);
}
}
}
else
{
ResetShowStruct(&s);
AddString(&s, textstrings[i]);
AddMoveto(&s, 0, textstart[i][0], textstart[i][1]);
timing_info.kerns += showstructure(font, textstrings[i], &s, justify,
kern, track);
ShowAny(&s);
}
}
}
PSWReturnTime(&ElapsedTime);
if (trace)
DPSTraceContext(DPSGetCurrentContext(), NO);
PSsetwindowtype(NX_BUFFERED, [window windowNum]);
timing_info.time += ElapsedTime;
timing_info.reshows++;
return self;
}
/*
* Erase the fields in the interface, whiten the backdrop, check the cases
* for showing the text and call the showText: method appropriately.
*/
- drawSelf:(const NXRect *)r :(int) count
{
BOOL temp;
if (eraseFields)
[self eraseFields:self];
PSsetgray(NX_WHITE);
NXRectFill(r);
PSsetgray(NX_BLACK);
if (track)
PStranslate(-45, 0);
if (compareKern)
{
temp = kern;
kern = YES;
[self showText:r];
kern = NO;
PStranslate(0.0, 10.0);
[self showText:r];
kern = temp;
}
else if (compareWidths)
{
temp = screen;
screen = NO;
[self showText:r];
screen = YES;
PStranslate(0.0, 10.0);
[self showText:r];
screen = temp;
}
else
[self showText:r];
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.