This is IGraphicImage.m in view mode; [Download] [Up]
/*$Copyright:
* Copyright (C) 1992.5.22. Recruit Co.,Ltd.
* Institute for Supercomputing Research
* All rights reserved.
* NewsBase by ISR, Kazuto MIYAI, Gary ARAKAKI, Katsunori SUZUKI, Kok-meng Lue
*
* You may freely copy, distribute and reuse the code in this program under
* following conditions.
* - to include this notice in the source code, if it is to be distributed
* with source code.
* - to add the file named "COPYING" within the code, which shall include
* GNU GENERAL PUBLIC LICENSE(*).
* - to display an acknowledgement in binary code as follows: "This product
* includes software developed by Recruit Co.,Ltd., ISR."
* - to display a notice which shall state that the users may freely copy,
* distribute and reuse the code in this program under GNU GENERAL PUBLIC
* LICENSE(*)
* - to indicate the way to access the copy of GNU GENERAL PUBLIC LICENSE(*)
*
* (*)GNU GENERAL PUBLIC LICENSE is stored in the file named COPYING
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
$*/
//*****************************************************************************
// IGraphicImage is used to implement an embedded graphic object as specified
// by Text class. An embedded grahic image object is implemented by two
// classes IGraphicImage and a media class. The IGraphicImage class implements
// the generic methods and the media class implements the media specific
// methods. The IGraphicImage object has a link to the supporting media
// object and will delegate media specific operations to the media object.
// Note that more than one IGraphicImage object can share the same media
// object. Thw IGraphicImage class also has methods for supporting mouse
// events over their corresponding images. These include mouse events to
// resize/subimage an image and drag out media objects. This class now
// also allows documents to be expanded and then replace the selection.
//*****************************************************************************
#import <dpsclient/psops.h>
#import <dpsclient/wraps.h>
#import <appkit/NXImage.h>
#import <appkit/NXBitmapImageRep.h>
#import <appkit/Text.h>
#import <appkit/Window.h>
#import <appkit/Application.h>
#import <sys/param.h> /* added in for MAXPATHLEN */
#import <streams/streams.h>
#import <sys/types.h>
#import <sys/stat.h>
#import <c.h>
#import <libc.h>
#import <strings.h>
#import <objc/zone.h>
#import <mach/mach.h>
#import <ctype.h>
#import <objc/HashTable.h>
#import <appkit/timer.h>
#import <appkit/Panel.h>
#import <appkit/tiff.h>
#import "Object1.h"
#import "IGraphicImage.h"
#import "JFIFBitmap.h"
#import "IArticleD.h"
#import "IMMEditor.h"
#import "IMediaTable.h"
#import "IMediaD.h"
#import "INewsBaseText.h"
#import "IFileIconButton.h"
#import "IBinaryD.h"
#import "IExternalD.h"
#import "ILocalFileD.h"
#import "IJfifD.h"
#import "ITiffD.h"
#import "IConvertMIME.h"
#import "data_types.h"
#import "drawRect.h"
#import "errdebug.h"
#import "Localization.h"
#define LoStr(key) doLocalString(NULL,key,NULL)
// ## REWRITE! ## might be better to send a message than use extern
extern HashTable *listOfTempFiles;
// list points to a list that will contain the media objects written by
// writeRichText: message
static List *list = nil;
static char fileExtensions[128];
static int *counter = NULL;
#define SIZE(N) (((N + 2) / 3 * 4 + 75) / 76 * 78)
@implementation IGraphicImage
+ initialize
{
int i;
Class mediaClass;
const char *sptr;
char *tptr;
tptr = fileExtensions;
for (i = 0; (mediaClass = [IMediaTable mediaClassAt:i]) != Nil; ++i) {
sptr = [(id)mediaClass fileExtension];
if (strcmp(sptr, "rtf") == 0 || strcmp(sptr, "tbl") == 0) {
continue;
}
for (; *sptr != '\0' && tptr <
&fileExtensions[sizeof(fileExtensions) - 1]; ++sptr, ++tptr) {
*tptr = *sptr;
}
*tptr++ = ',';
}
*--tptr = '\0';
return(self);
}
+ setCounter:(int *)aCounter
{
counter = aCounter;
return(self);
}
// list contains the media objects used by the writeRichText: message
+ setList:aList
{
list = aList;
return(self);
}
//*****************************************************************************
// Create IGraphicImage from file
//*****************************************************************************
- (IGraphicImage *)initFromFile:(const char *)pathName forView:(View *)view
withIcon:(NXImage *)icon
// Be careful because ownership of icon is transferred to IGraphicImage object.
{
const char *fileName, *extension;
char mediaObjectName[MAXPATHLEN + 16];
Class mediaClass;
IArticleD *tempArticle;
int i;
IMediaD *insertObject;
NXStream *stream;
ITextD *textObject;
IConvertMIME *conv;
enum {UNDEFINED, EXPAND, EMBED, EXTERNAL} option = UNDEFINED;
text = (INewsBaseText *)view;
if ((fileName = rindex(pathName, '/')) == 0) {
fileName = pathName;
} else {
++fileName;
}
if ((extension = rindex(pathName, '/'), extension = rindex(extension, '.'))
!= 0) {
++extension;
} else {
extension = [IBinaryD fileExtension];
}
// document files can be expanded to replace selection or be embedded and
// displayed as an icon or be made into a external reference and displayed
// as an icon.
if (strcmp(extension, RTF_FILE_EXTENSION) == 0 ||
strcmp(extension, MM_FILE_EXTENSION) == 0 ||
strcmp(extension, MIME_FILE_EXTENSION) == 0 ||
strcmp(extension, NEWS_FILE_EXTENSION) == 0) {
switch(NXRunAlertPanel(LoStr(MMEDITOR),
LoStr("How should object be included?"), LoStr("Expand"),
LoStr("Embed"), LoStr("External"))) {
case NX_ALERTDEFAULT:
// Expand and Replace selection is a special case
option = EXPAND;
if (strcmp(extension, RTF_FILE_EXTENSION) == 0) {
stream = NXMapFile(pathName, NX_READONLY);
[text replaceSelWithRichText:stream];
NXCloseMemory(stream, NX_FREEBUFFER);
} else {
// .mmd directory or .news file so make a temporary article
tempArticle = [ILocalFileD loadFromName:pathName
inZone:[[text article] zone]];
if ((textObject = [tempArticle dataForKey:INDEX_RTF])
!= nil) {
// loop over media objects in temporary article
for (i = [tempArticle count] - 1; i >= 0 &&
(insertObject = [tempArticle objectAt:i]) != nil;
--i) {
if ([insertObject isKindOf:[IMediaD class]] == TRUE &&
[[text article] dataForKey:[insertObject key]]
== nil) {
// put media object in new article and remove from
// temporary article
[[text article] insertKeyedObject:insertObject];
[tempArticle removeObject:insertObject];
}
}
// now replace selection with text
stream = NXOpenMemory([textObject textData],
[textObject size], NX_READONLY);
[text replaceSelWithRichText:stream];
NXCloseMemory(stream, NX_SAVEBUFFER);
} else {
// replace selection with plain text
textObject = [tempArticle dataForKey:PLAINTEXT];
[text replaceSel:[textObject textData]
length:[textObject size]];
}
// free temporary article
[tempArticle free];
}
// in this special case no graphic object is returned
[icon free];
return([self free]);
case NX_ALERTALTERNATE:
// Embed or External is handled later
option = EMBED;
break;
case NX_ALERTOTHER:
// Embed or External is handled later
option = EXTERNAL;
break;
}
}
if (strcmp(extension, "tbl") == 0) {
NXRunAlertPanel(LoStr("NewsBase"),
LoStr("Filename %s has bad extension"),NULL,NULL,NULL, fileName);
return([self free]);
}
strncpy(mediaObjectName, fileName, sizeof(mediaObjectName));
STRDBG(mediaObjectName);
// check if media object is already defined and create only if not defined
if ((mediaObject = [[text article] dataForKey:mediaObjectName]) == nil) {
if (option == UNDEFINED) {
switch(NXRunAlertPanel(LoStr(MMEDITOR),
LoStr("How is object to be included?"),
LoStr("Embed"), LoStr("External"), NULL)) {
case NX_ALERTDEFAULT:
option = EMBED;
break;
case NX_ALERTALTERNATE:
option = EXTERNAL;
break;
}
}
switch (option) {
case EMBED:
// embed as binary and show icon
/*
if (strcmp(extension, MM_FILE_EXTENSION) == 0 ||
strcmp(extension, MIME_FILE_EXTENSION) == 0 ||
strcmp(extension, NEWS_FILE_EXTENSION) == 0) {
NXRunAlertPanel(LoStr(EDITOR_APP_NAME),
LoStr("This capability currently not implemented."),
NULL,NULL,NULL);
[icon free];
return([self free]);
}
*/
// rtf, mmd, mime and news files must be handled as binary
if (strcmp(extension, "rtf") != 0 &&
strcmp(extension, MM_FILE_EXTENSION) != 0 &&
strcmp(extension, MIME_FILE_EXTENSION) != 0 &&
strcmp(extension, NEWS_FILE_EXTENSION) != 0 &&
(mediaClass = [IMediaTable mediaClassForFileExtension:
extension]) != Nil &&
mediaClass != (Class)[IBinaryD class]) {
mediaObject = [(id)mediaClass allocFromZone:
[[text article] zone]];
[mediaObject initWithKey:mediaObjectName];
if ([mediaObject readFromFile:pathName] == YES) {
[[text article] insertKeyedObject:mediaObject];
} else {
[icon free];
return([self free]);
}
[icon free];
} else {
mediaObject = [IBinaryD allocFromZone: [[text article] zone]];
// ownership of icon transferred to mediaObject
[mediaObject setImage:icon];
// mmd requires special handling since it is not a file
if (strcmp(extension, MM_FILE_EXTENSION) == 0) {
// change extension from .mmd to .mime
strcpy(rindex(mediaObjectName, '.') + 1,
MIME_FILE_EXTENSION);
[mediaObject initWithKey:mediaObjectName];
// convert mmd directory to MIME file
tempArticle = [ILocalFileD loadFromName:pathName
inZone:[[text article] zone]];
[tempArticle setObjectCount:[tempArticle count] - 2];
conv = [[IConvertMIME alloc] init];
stream = NXOpenMemory(NULL, 0, NX_READWRITE);
[conv convertToMIME:tempArticle stream:stream];
[conv free];
[tempArticle free];
NXSeek(stream, (long)0, NX_FROMSTART);
if ([mediaObject readFromStream:stream] == YES) {
[[text article] insertKeyedObject:mediaObject];
NXCloseMemory(stream, NX_FREEBUFFER);
} else {
NXCloseMemory(stream, NX_FREEBUFFER);
[icon free];
return([self free]);
}
} else {
[mediaObject initWithKey:mediaObjectName];
if ([mediaObject readFromFile:pathName] == YES) {
[[text article] insertKeyedObject:mediaObject];
} else {
[icon free];
return([self free]);
}
}
}
break;
case EXTERNAL:
// make an external reference and show icon
if (strcmp(extension, MM_FILE_EXTENSION) != 0 &&
strcmp(extension, MIME_FILE_EXTENSION) != 0 &&
strcmp(extension, NEWS_FILE_EXTENSION) != 0) {
NXRunAlertPanel(LoStr(EDITOR_APP_NAME),
LoStr("This capability currently not implemented."),
NULL,NULL,NULL);
[icon free];
return([self free]);
}
mediaObject = [[IExternalD
allocFromZone:NXCreateZone(vm_page_size, vm_page_size, YES)]
initWithDomain:[ILocalFileD domain] andPath:pathName];
[[[text article] externalsList] addObjectIfAbsent:mediaObject];
[icon free];
break;
case EXPAND:
case UNDEFINED:
// needed to supress compiler warnings only
break;
}
} else {
[icon free];
}
[mediaObject incrementReferenceCount];
[text addToArticleSize:SIZE([mediaObject size])];
// if ([mediaObject incrementReferenceCount] == 1) {
// [text addToArticleSize:SIZE([mediaObject size])];
// }
image = [mediaObject image];
[[[text article] objectsList] addObject:self];
return(self);
}
//*****************************************************************************
// Create IGraphicImage from pasteboard
//*****************************************************************************
- (IGraphicImage *)initFromPasteboard:(Pasteboard *)pasteboard
forView:(INewsBaseText *)view
{
const NXAtom *pasteboardType;
char mediaObjectName[1024];
Class mediaClass;
char *ptr;
BOOL status;
id loadingAlertPanel;
NXModalSession loadingModalSession;
text = view;
for (pasteboardType = [pasteboard types]; *pasteboardType != NULL;
++pasteboardType) {
if ((mediaClass = [IMediaTable mediaClassForPasteboardType:
*pasteboardType]) != Nil) {
break;
}
}
if (*pasteboardType == NULL) {
return([self free]);
}
if ([text isMultimedia] == NO) {
switch(NXRunAlertPanel(LoStr("NewsBase"),LoStr("This is not a multimedia article. Change to multimedia?"),LoStr("YES"),LoStr("NO"),NULL)) {
case NX_ALERTDEFAULT:
[text setIsMultimedia:YES];
break;
case NX_ALERTALTERNATE:
return([self free]);
}
}
sprintf(mediaObjectName, "%.256s_%d.%s", [pasteboard name],
[pasteboard changeCount], [(id)mediaClass fileExtension]);
for (ptr = mediaObjectName; *ptr != '\0'; ++ptr) {
if (isalnum(*ptr) == 0 && *ptr != '.') {
*ptr = '_';
}
}
if ((mediaObject = [[text article] dataForKey:mediaObjectName]) == nil) {
mediaObject = [[(id)mediaClass allocFromZone:[[text article] zone]]
initWithKey:mediaObjectName];
loadingAlertPanel = NXGetAlertPanel(LoStr("NewsBase"),
LoStr("Loading from %s pasteboard..., please wait")
, NULL, NULL, NULL,[(id)mediaClass pasteboardType]);
[NXApp beginModalSession:&loadingModalSession for:loadingAlertPanel];
status = [mediaObject readFromPasteboard:pasteboard];
[NXApp runModalSession:&loadingModalSession];
[NXApp endModalSession:&loadingModalSession];
[loadingAlertPanel orderOut:self];
NXFreeAlertPanel(loadingAlertPanel);
if (status == YES && [mediaObject class] != [IExternalD class]) {
[[text article] insertKeyedObject:mediaObject];
} else {
return([self free]);
}
}
[mediaObject incrementReferenceCount];
[text addToArticleSize:SIZE([mediaObject size])];
// if ([mediaObject incrementReferenceCount] == 1) {
// [text addToArticleSize:SIZE([mediaObject size])];
// }
if ([mediaObject class] == [IExternalD class]) {
[[[text article] externalsList] addObjectIfAbsent:mediaObject];
}
image = [mediaObject image];
[[[text article] objectsList] addObject:self];
return(self);
}
- free
{
List *objectsList;
id object;
int i;
BOOL multipleReferenceExists;
[[[text article] objectsList] removeObject:self];
[text addToArticleSize:-SIZE([mediaObject size])];
// ## REWRITE! ## multiple external references will not be handled
// correctly but this yet another reference count (must find a better way!)
// Handle multiple references for external objects.
if ([mediaObject class] == [IExternalD class]) {
// check if there is another reference to this media object
multipleReferenceExists = NO;
objectsList = [[text article] objectsList];
for (i = 0; (object = [objectsList objectAt:i]) != nil; ++i) {
if (mediaObject == [object mediaObject]) {
multipleReferenceExists = YES;
break;
}
}
if (multipleReferenceExists == NO) {
[[[text article] externalsList] removeObject:mediaObject];
[mediaObject free];
mediaObject = nil;
}
}
// There are two cases where the graphics image object is freeded.
// The graphic image object is being deleted using the editor and
// the underlying text object is being freeded. In the first case
// the underlying media object should be freeded in the second
// case the underlying media object should be retained with a zero
// reference count as the article may be externally referenced.
// The only exception are externals because they are not in the
// IArticleD.
if (mediaObject != nil && [text textIsFreeing] == NO &&
[mediaObject decrementReferenceCount] == 0) {
// [text addToArticleSize:-SIZE([mediaObject size])];
[[text article] removeObject:mediaObject];
[mediaObject free];
}
return [super free];
}
- calcCellSize:(NXSize *)theSize
{
/* our graphic image determines our size */
[image getSize:theSize];
return self;
}
- highlight:(const NXRect *)cellFrame inView:controlView lit:(BOOL)flag
{
if (highlighted != flag) {
highlighted = flag;
/* toggle highlighting */
// NXHighlightRect(cellFrame);
/* make change visible */
[[controlView window] flushWindow];
}
return self;
}
//*****************************************************************************
// draw image in text view
//*****************************************************************************
#define RESIZE_BUTTON_SIZE 15
- drawSelf:(const NXRect *)cellFrame inView:controlView
{
NXPoint point;
NXRect resizeButtonRect;
char buffer[64];
NXSize size;
origin = cellFrame->origin;
origin.y += cellFrame->size.height;
/*
* the text object expects us not to modify the current graphics state, so
* we'll save it
*/
PSgsave();
/* clear our rect using the text object's background gray and transparancy */
PSsetgray([controlView backgroundGray]);
if ([controlView isOpaque]) {
PSsetalpha(1);
} else {
PSsetalpha(0);
}
NXRectFill(cellFrame);
/* we're in a flipped coordinate system */
point = cellFrame->origin;
point.y += cellFrame->size.height;
/* draw the image */
[image composite:NX_SOVER toPoint:&point];
// check if this image is currently selected for resizing or subimaging
if (imageResizingEnabled == YES) {
// resizing mode on so draw resizing button
// first draw larger white square in lower right corner
resizeButtonRect.origin.x = cellFrame->origin.x
+ cellFrame->size.width - RESIZE_BUTTON_SIZE;
resizeButtonRect.origin.y = cellFrame->origin.y
+ cellFrame->size.height - RESIZE_BUTTON_SIZE;
resizeButtonRect.size.width = resizeButtonRect.size.height =
RESIZE_BUTTON_SIZE;
PSsetgray(NX_WHITE);
NXRectFill(&resizeButtonRect);
// then draw smaller black square in lower right corner
resizeButtonRect.origin.x = cellFrame->origin.x
+ cellFrame->size.width - (2 * RESIZE_BUTTON_SIZE / 3);
resizeButtonRect.origin.y = cellFrame->origin.y
+ cellFrame->size.height - (2 * RESIZE_BUTTON_SIZE / 3);
resizeButtonRect.size.width = resizeButtonRect.size.height =
2 * RESIZE_BUTTON_SIZE / 3;
PSsetgray(NX_BLACK);
NXRectFill(&resizeButtonRect);
// now display image charecteristics in upper left corner
[image getSize:&size];
if (size.width > 100.0 && size.height > 100.0) {
// paint background white
PSsetgray(NX_WHITE);
// PSsetrgbcolor(0.0, 255.0, 0.0);
PSrectfill(cellFrame->origin.x, cellFrame->origin.y, 72.0, 48.0);
// show file type
sprintf(buffer, "%s", [[mediaObject class] fileExtension]);
PSmoveto((float)(cellFrame->origin.x + 2.0),
(float)(cellFrame->origin.y + 14.0));
PSsetgray(NX_BLACK);
PSshow(buffer);
// file size
sprintf(buffer, "%dbytes", [mediaObject size]);
PSmoveto((float)(cellFrame->origin.x + 2.0),
(float)(cellFrame->origin.y + 31.0));
PSshow(buffer);
// image dimensions
sprintf(buffer, "%.0f x %.0f", size.width, size.height);
PSmoveto((float)(cellFrame->origin.x + 2.0),
(float)(cellFrame->origin.y + 48.0));
PSshow(buffer);
PSsetrgbcolor(0.0, 0.0, 0.0);
}
}
if (isActiveEmbeddedView) {
drawRect((float)(cellFrame->origin.x), (float)(cellFrame->origin.y),
(float)(cellFrame->size.width), (float)(cellFrame->size.height),
0.5, 10);
}
/* restore the graphics state */
PSgrestore();
return self;
}
//*****************************************************************************
// Handle mouse events over image of IGraphicImage
//*****************************************************************************
// Mouse events can specify:
// 1) toggle on/off resize/subimage mode - double click on graphic
// 2) play/open object - double click on icon
// 3) drag and drop from text - mouse down and drag
// 4) resize (in resize/subimage mode) - mouse down and drag on button
// 5) subimage (in resize/subimage mode) - mouse down and drag in image
- trackMouse:(NXEvent *)theEvent inRect:(const NXRect *)cellFrame
ofView:controlView
{
NXEvent initialMouseEvent;
NXPoint initialMouseLocation;
int oldMask;
NXEvent *event, *nextEvent;
NXPoint mouseLocation;
BOOL mouseInCell = NO;
NXRect rect;
char stringBuffer[512];
const char *filename;
NXStream *stream;
NXTrackingTimer timer;
NXRect resizeButtonRect;
double x0, y0;
enum {RESIZE, EDIT} operation;
NXRect sourceRect, targetRect;
NXBitmapImageRep *sourceBitmap;
JFIFBitmap *tempBitmap;
NXImage *targetImage;
NXStream *targetStream;
ITiffD *tiffObject;
char tiffObjectName[512], *ptr;
static int resizeCount = 0;
NXRect frame;
float heightToWidthRatio;
NXPoint displayPoint;
const char *option;
const char *factorString;
int factor;
id compressingAlertPanel;
NXModalSession compressingModalSession;
char buffer[64];
initialMouseEvent = *theEvent;
initialMouseLocation = initialMouseEvent.location;
[controlView convertPoint:&initialMouseLocation fromView:NULL];
mouseLocation = initialMouseLocation;
if (initialMouseEvent.type == NX_MOUSEDOWN &&
initialMouseEvent.data.mouse.click == 1) {
if (imageResizingEnabled == YES) {
// Image resize/subimage mode has been toggled on so
// this is a resize or subimage operation
// check if mouse down is in resize rectangle or elsewhere in
// image to determine operation type
resizeButtonRect.origin.x = cellFrame->origin.x
+ cellFrame->size.width - RESIZE_BUTTON_SIZE;
resizeButtonRect.origin.y = cellFrame->origin.y
+ cellFrame->size.height - RESIZE_BUTTON_SIZE;
resizeButtonRect.size.width = resizeButtonRect.size.height =
RESIZE_BUTTON_SIZE;
if (NXPointInRect(&mouseLocation, &resizeButtonRect) == YES) {
// mouse down in resize button and drag is a resize operation
operation = RESIZE;
x0 = cellFrame->origin.x;
y0 = cellFrame->origin.y;
heightToWidthRatio =
cellFrame->size.height / cellFrame->size.width;
} else {
// mouse down in rest of image is a subimage operation
operation = EDIT;
x0 = mouseLocation.x;
y0 = mouseLocation.y;
}
oldMask = [[controlView window]
addToEventMask:NX_MOUSEDRAGGEDMASK];
[controlView lockFocus];
event = [NXApp getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK];
while (event->type != NX_MOUSEUP) {
[controlView display:NULL :0];
mouseLocation = event->location;
[controlView convertPoint:&mouseLocation fromView:NULL];
if (mouseLocation.x - x0 > 0.0 && mouseLocation.y - y0 > 0.0) {
if (operation == RESIZE) {
// draw a gray rectangle that preserves the aspect
// (height / width) ratio with new width
drawRect((float)x0, (float)y0,
(float)(mouseLocation.x - x0),
(float)(heightToWidthRatio *
(mouseLocation.x - x0)), 0.5, 5);
}
// draw new rectangle in black with white border
drawRect((float)x0, (float)y0,
(float)(mouseLocation.x - x0),
(float)(mouseLocation.y - y0), 1.0, 5);
drawRect((float)x0, (float)y0,
(float)(mouseLocation.x - x0),
(float)(mouseLocation.y - y0), 0.0, 3);
// show new length and width in purple
PSsetgray(NX_WHITE);
// PSsetrgbcolor(0.0, 255.0, 0.0);
PSrectfill(mouseLocation.x - 62.0, mouseLocation.y - 17.0,
62.0, 17.0);
sprintf(buffer, "%.0f x %.0f", mouseLocation.x - x0,
mouseLocation.y - y0);
PSmoveto((float)(mouseLocation.x - 60.0),
(float)(mouseLocation.y - 5.0));
PSsetgray(NX_BLACK);
// PSsetrgbcolor(255.0, 0.0, 255.0);
PSshow(buffer);
PSsetrgbcolor(0.0, 0.0, 0.0);
[[controlView window] flushWindow];
}
NXPing();
event = [NXApp getNextEvent:NX_MOUSEUPMASK |
NX_MOUSEDRAGGEDMASK];
// skip some mouse dragged events for better performance
while (event->type != NX_MOUSEUP && (nextEvent =
[NXApp getNextEvent:NX_MOUSEUPMASK |
NX_MOUSEDRAGGEDMASK waitFor:0.0
threshold:NX_MODALRESPTHRESHOLD]) != NULL) {
event = nextEvent;
}
}
[controlView unlockFocus];
// determine the new size or subimage from initial and final
// mouse location
switch (operation) {
case RESIZE:
sourceRect.origin.x = sourceRect.origin.y = 0.0;
[image getSize:&sourceRect.size];
targetRect.size.width = mouseLocation.x - cellFrame->origin.x;
targetRect.size.height = mouseLocation.y - cellFrame->origin.y;
break;
case EDIT:
sourceRect.origin.x = initialMouseLocation.x - cellFrame->origin.x;
sourceRect.origin.y = cellFrame->size.height - (mouseLocation.y
- cellFrame->origin.y);
sourceRect.size.width = mouseLocation.x -
initialMouseLocation.x;
sourceRect.size.height = mouseLocation.y -
initialMouseLocation.y;
targetRect.size = sourceRect.size;
break;
}
targetRect.origin.x = targetRect.origin.y = 0.0;
// if mouse moved to left or up interpret that as a cancel
// request by the user
if (targetRect.size.width <= 0.0 || targetRect.size.height <= 0.0 ||
sourceRect.size.width <= 0.0 || sourceRect.size.height <= 0.0) {
return(self);
}
imageResizingEnabled = NO;
// get source image
[image lockFocus];
sourceBitmap = [[NXBitmapImageRep allocFromZone:
[[text article] zone]] initData:NULL fromRect:&sourceRect];
[image unlockFocus];
// resize (maybe)
targetImage = [[NXImage allocFromZone:[[text article] zone]]
initSize:&targetRect.size];
[targetImage useCacheWithDepth:NX_TwentyFourBitRGBDepth];
[targetImage lockFocus];
[sourceBitmap drawIn:&targetRect];
[targetImage unlockFocus];
[sourceBitmap free];
// display temporary image
[controlView lockFocus];
switch (operation) {
case RESIZE:
displayPoint.x = cellFrame->origin.x;
displayPoint.y = mouseLocation.y;
break;
case EDIT:
displayPoint.x = initialMouseLocation.x;
displayPoint.y = mouseLocation.y;
break;
}
PSsetgray([controlView backgroundGray]);
if ([controlView isOpaque]) {
PSsetalpha(1);
} else {
PSsetalpha(0);
}
NXRectFill(cellFrame);
[targetImage composite:NX_SOVER toPoint:&displayPoint];
drawRect((float)x0, (float)y0, (float)(mouseLocation.x - x0),
(float)(mouseLocation.y - y0), 0.0, 5);
[controlView unlockFocus];
[[controlView window] flushWindow];
// now make compressed stream
[targetImage lockFocus];
tempBitmap = [[JFIFBitmap allocFromZone:[[text article] zone]]
initData:NULL fromRect:&targetRect];
[targetImage unlockFocus];
[targetImage free];
targetStream = NXOpenMemory(NULL, 0, NX_READWRITE);
if ((option = NXGetDefaultValue(OWNER, JFIF_OR_TIFF)) != 0
&& strcmp(option, "TIFF") == 0) {
if ([tempBitmap bitsPerSample] >= 4) {
// JPEG in TIFF
if ((factorString = NXGetDefaultValue(OWNER,
JPEGCOMPRESSIONFACTOR)) != 0) {
factor = atoi(factorString);
} else {
factor = 10;
}
compressingAlertPanel = NXGetAlertPanel(LoStr(MMEDITOR),
LoStr("Compressing using JPEG to TIFF with factor"
" %s... please wait"), NULL, NULL, NULL, factorString);
[NXApp beginModalSession:&compressingModalSession
for:compressingAlertPanel];
[tempBitmap writeTIFF:targetStream
usingCompression:NX_TIFF_COMPRESSION_JPEG
andFactor:(float)factor];
} else {
// not enough depth for jpeg do lzw instead
compressingAlertPanel = NXGetAlertPanel(LoStr(MMEDITOR),
LoStr("Compressing using LZW to TIFF..."
" please wait"), NULL, NULL, NULL);
[NXApp beginModalSession:&compressingModalSession
for:compressingAlertPanel];
[tempBitmap writeTIFF:targetStream
usingCompression:NX_TIFF_COMPRESSION_LZW
andFactor:0.0];
}
tiffObject = [ITiffD allocFromZone:[[text article] zone]];
} else {
if ([tempBitmap bitsPerSample] == 8) {
// JPEG
if ((factorString = NXGetDefaultValue(OWNER,
JPEGCOMPRESSIONFACTOR)) != 0) {
factor = atoi(factorString);
} else {
factor = 75;
}
compressingAlertPanel = NXGetAlertPanel(LoStr(MMEDITOR),
LoStr("Compressing to JPEG with factor"
" %s... please wait"), NULL, NULL, NULL, factorString);
[NXApp beginModalSession:&compressingModalSession
for:compressingAlertPanel];
[tempBitmap writeJFIF:targetStream usingQuality:factor];
tiffObject = [IJfifD allocFromZone:[[text article] zone]];
} else {
// not enough depth for jpeg do lzw instead
compressingAlertPanel = NXGetAlertPanel(LoStr(MMEDITOR),
LoStr("Compressing using LZW to TIFF..."
" please wait"), NULL, NULL, NULL);
[NXApp beginModalSession:&compressingModalSession
for:compressingAlertPanel];
[tempBitmap writeTIFF:targetStream
usingCompression:NX_TIFF_COMPRESSION_LZW
andFactor:0.0];
tiffObject = [ITiffD allocFromZone:[[text article] zone]];
}
}
[tempBitmap free];
strncpy(tiffObjectName, [mediaObject key],
sizeof(tiffObjectName) - 32);
tiffObjectName[sizeof(tiffObjectName) - 32] = '\0';
if ((ptr = rindex(tiffObjectName, '.')) != 0) {
*ptr = '\0';
}
if ((ptr = rindex(tiffObjectName, '#')) != 0) {
*ptr = '\0';
}
ptr = index(tiffObjectName, '\0');
// must give this a object a new name; use sequence no to insure
// uniqueness
sprintf(ptr, "#%d.%.16s", resizeCount++,
[[tiffObject class] fileExtension]);
[tiffObject initWithKey:tiffObjectName];
if ([tiffObject readFromStream:targetStream] == YES) {
[text addToArticleSize:-SIZE([mediaObject size])];
if (mediaObject != nil &&
[mediaObject decrementReferenceCount] == 0) {
// [text addToArticleSize:-SIZE([mediaObject size])];
[[text article] removeObject:mediaObject];
[mediaObject free];
}
mediaObject = tiffObject;
[[text article] insertKeyedObject:mediaObject];
[mediaObject incrementReferenceCount];
[text addToArticleSize:SIZE([mediaObject size])];
image = [mediaObject image];
[controlView getFrame:&frame];
[controlView calcLine];
[compressingAlertPanel orderOut:self];
[NXApp endModalSession:&compressingModalSession];
NXFreeAlertPanel(compressingAlertPanel);
[controlView display:NULL :0];
[controlView setNeedsSaving:YES];
} else {
imageResizingEnabled = YES;
[controlView display:NULL :0];
}
NXCloseMemory(targetStream, NX_FREEBUFFER);
return(self);
} else {
// drag & drop or first click of double click
/* we want to grab mouse dragged events */
oldMask = [[controlView window] addToEventMask:NX_MOUSEDRAGGEDMASK];
// look for mouse-up, mouse-dragged or tiemout events
// need artificial events for timeout to work
NXBeginTimer(&timer, 0.25, 0.1);
event = [NXApp getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK
| NX_TIMERMASK];
while (event->type != NX_MOUSEUP && event->type != NX_TIMER) {
/* mouse-dragged event; highlight if mouse is in cell bounds */
mouseLocation = event->location;
[controlView convertPoint:&mouseLocation fromView:NULL];
mouseInCell = NXPointInRect(&mouseLocation, cellFrame);
if (mouseInCell != highlighted) {
/* we have to lock focus before calling hightlight:inView:lit:*/
[controlView lockFocus];
[self highlight:cellFrame inView:controlView lit:mouseInCell];
[controlView unlockFocus];
}
if (mouseInCell == NO) {
break;
}
event = [NXApp getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK
| NX_TIMERMASK];
}
NXEndTimer(&timer);
/* turn off any highlighting */
[controlView lockFocus];
[self highlight:cellFrame inView:controlView lit:NO];
[controlView unlockFocus];
/* reset the event mask */
[[controlView window] setEventMask:oldMask];
if (event->type != NX_MOUSEUP) {
// drag & drop
if ([mediaObject class] == [IExternalD class]) {
return(self);
}
sprintf(stringBuffer, "/tmp/%.506s", [mediaObject key]);
filename = NXCopyStringBuffer(stringBuffer);
stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
[mediaObject writeToStream:stream];
NXSaveToFile(stream, filename);
// Add executable permission if neccessary
sprintf(stringBuffer, "file %s | grep executable > /dev/null",
filename);
if (system(stringBuffer) == 0) {
chmod(filename, 0700);
} else {
chmod(filename, 0600);
}
NXCloseMemory(stream, NX_FREEBUFFER);
rect.origin.x = mouseLocation.x - 24.0;
rect.origin.y = mouseLocation.y - 24.0;
rect.size.width = 48.0;
rect.size.height = 48.0;
[controlView dragFile:filename fromRect:&rect slideBack:YES
event:&initialMouseEvent];
[controlView setOwnerOfDraggedIconToMe:YES];
// ## REWRITE! ## very bad to use externs
// schedule file for deletion
[listOfTempFiles insertKey:filename value:nil];
[IFileIconButton perform:@selector(unlink:)
with:(id)filename afterDelay:600000 cancelPrevious:NO];
}
return(self);
}
} else if (NXPointInRect(&mouseLocation, cellFrame) &&
initialMouseEvent.data.mouse.click == 2) {
/* if a double-click and the mouse is over us, do something */
if ([mediaObject performDoubleClickAction:self] == nil) {
// this is an image so toggle resizing
if (imageResizingEnabled == NO) {
if ([controlView isEditable] == NO ||
[controlView hasEmbeddedView] == YES) {
if ([[controlView delegate] textWillChange:self] == YES) {
return(self);
}
}
imageResizingEnabled = YES;
[controlView display:NULL :0];
} else {
imageResizingEnabled = NO;
[controlView display:NULL :0];
}
}
return(self);
}
return(self);
}
//*****************************************************************************
// create IGraphicImage from rtf stream
//*****************************************************************************
- readRichText:(NXStream *)stream forView:view
{
char mediaObjectName[MAXPATHLEN + 16];
const char *fileName;
char path[MAXPATHLEN];
Class MediaClass;
const char *extension;
text = view;
NXScanf(stream, "%[^\n]", mediaObjectName);
STRDBG(mediaObjectName);
NXScanf(stream, "%*[\n]");
if ([text isKindOf:[INewsBaseText class]] == NO) {
NXRunAlertPanel(LoStr("NewsBase"),
LoStr("Paste failed.Text object cannot read graphic object."),
NULL,NULL,NULL);
mediaObject = nil;
image = nil;
return(self);
}
if ((fileName = rindex(mediaObjectName, '/')) == 0) {
fileName = mediaObjectName;
} else {
++fileName;
}
// Internal media objects must already exists but external objects may not
// already exists so check if external object already exists and create
// if doesn't. External Objects do not respond to readFromStream
// message and this may be used to differentiate them from internal
// media objects.
if ((mediaObject = [[text article] dataForKey:fileName]) == nil) {
// object doesn't exists
extension = rindex(mediaObjectName, '.');
if (extension != 0 && (MediaClass =
[IMediaTable mediaClassForFileExtension:
NXUniqueString(extension + 1)]) != Nil &&
[(id)MediaClass instancesRespondTo:@selector(readFromStream:)]
== NO) {
// non-existant external object so create it
strcpy(path, mediaObjectName);
*rindex(path, '.') = '\0';
mediaObject = [[IExternalD
allocFromZone:NXCreateZone(vm_page_size, vm_page_size, YES)]
initWithDomain:[(id)MediaClass domain] andPath:path];
[[[text article] externalsList] addObjectIfAbsent:mediaObject];
} else {
// non-existant internal object is an error
NXRunAlertPanel(LoStr("NewsBase"),LoStr("no Media object for %s"),
NULL,NULL,NULL, fileName);
mediaObject = nil;
image = nil;
return(self);
}
}
[mediaObject incrementReferenceCount];
[text addToArticleSize:SIZE([mediaObject size])];
// if ([mediaObject incrementReferenceCount] == 1) {
// [text addToArticleSize:SIZE([mediaObject size])];
// }
image = [mediaObject image];
[[[text article] objectsList] addObject:self];
return(self);
}
// writeRichText:forView: Since the data for the object is separately stored
// just write out the name of the object.
- writeRichText:(NXStream *)stream forView:view
{
const char *key;
if (mediaObject == nil) {
NXPrintf(stream, "nil\n");
return(self);
}
if ((key = [mediaObject key]) != NULL) {
NXPrintf(stream, "%s\n", key);
} else {
NXPrintf(stream, "nil\n");
}
// maintain list of objects written
if (list != nil) {
[list addObjectIfAbsent:mediaObject];
}
if (counter != NULL) {
++*counter;
}
return(self);
}
- mediaObject
{
return(mediaObject);
}
- (View *)view
{
return((View *)text);
}
- getOrigin:(NXPoint *)anOrigin
{
*anOrigin = origin;
return self;
}
- setIsActiveEmbeddedView:(BOOL)flag
{
isActiveEmbeddedView = flag;
return(self);
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.