This is PictController.m in view mode; [Download] [Up]
/***********************************************************************\ Controller for Convert PICT which converts graphics from PICT to eps formats. Copyright (C) 1993 David John Burrowes This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. The author, David John Burrowes, can be reached at: davidjohn@kira.net.netcom.com David John Burrowes 1926 Ivy #10 San Mateo, CA 94403-1367 \***********************************************************************/ /* ==================================================================== This is $Revision: 1.10 $ of this file It was last modified by $Author: death $ on $Date: 93/04/04 23:30:20 $ Note that this file was created while using the New Century Schoolbook Roman typeface. You may find that some things line up strangely if you don't use that family. NOTE: This has a preference called something like USECLUTS. This is a feature that was dropped before the 1.0 version. I'm leaving the hooks in just in case I later wish to implement it. The gist of the feature was: given a bitmap or pattern with a color lookup table, substitute the looked up color values into the bitmap for the original indices. This would have the effect of making the resulting file ENORMOUSLY larger in size than the source. But, it would also be printable or displayable on a PS level 1 device (that had the common color extensions, of course) $Log: PictController.m,v $ Revision 1.10 93/04/04 23:30:20 death Sun Apr 4 23:30:20 PDT 1993 Revision 1.9 93/01/09 21:07:17 death Sat Jan 9 21:07:17 PST 1993 Revision 1.8 93/01/01 11:51:24 death Fri Jan 1 11:51:24 PST 1993 Revision 1.7 92/12/31 15:33:52 death Thu Dec 31 15:33:52 PST 1992 Revision 1.6 92/12/05 23:06:22 death Sat Dec 5 23:06:21 PST 1992 ==================================================================== */ #import "PictController.h" #import "PSFile.h" #import "PICTFile.h" #import "PictConverter.h" #import <appkit/Matrix.h> // For matrix stuff for preferences setting #import "string.h" #import <appkit/Application.h> // For NXApp #import <appkit/Listener.h> // For NXApp @implementation PictController ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: init // Parameters: none // Returns: self // Stores: none // Description: // This overrides the defalut init method. and sets up some strings used for // interacting with the user. We also create the pict converter instance // needed later. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - init { static NXDefaultsVector theDefaults = { {PACKIMAGE, "YES"}, {USECLUTS, "NO"}, {VERBOSEPS, "NO"}, {CONVERTCHARS, "NO"}, {CONVERTPICC, DISCARDALLPICC}, {NULL, NULL} }; CString tempConvertString; [super init]; // // Provide silly default strings. // ConversionString = "Converting Mac PICT to NeXT eps"; SourcePrompt = "PICT file:"; DestPrompt = "EPS file:"; DestExtension = ".eps"; DefaultsOwner = "PICTConverter"; NXRegisterDefaults(DefaultsOwner, theDefaults); PackImage = [self GetBoolPref: PACKIMAGE]; FoldInCLUTs = [self GetBoolPref: USECLUTS]; UseVerbosePS = [self GetBoolPref: VERBOSEPS]; ConvertAllChars = [self GetBoolPref: CONVERTCHARS]; tempConvertString = [self GetPref: CONVERTPICC]; if (strcmp(tempConvertString, DISCARDALLPICC) == 0) CommentOperation = DiscardPicComments; else if (strcmp(tempConvertString, CONVERTALLPICC) == 0) CommentOperation = ConvertPicComments; else // An error occurred. But, let's just be silently lazy for the moment: CommentOperation = ConvertPicComments; FreeCString(tempConvertString); converterInst = [[PictConverter alloc] init]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: displayPreferences: // Parameters: the object that called us // Returns: self // Stores: none // This is called whenever we need to bring the preferences panel onto the screen. // Using the value in our several preferences variables, we set the buttons in // the preferences panel to reflect the current values, and then shows the panel. // History: // 93.07.18 djb Modified for Convert pict comment stuff. // Bugs: // As pointed out elsewhere, we don't really need to do this after the first time, // since the buttons keep their own state. So this paradigm needs revising!! ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -displayPreferences: target { // // Get the default preference values, and compare judge what buttons to // highlight for the user. // if (PackImage == YES) [CompressButton selectCellWithTag: 1]; else [CompressButton selectCellWithTag: 0]; if (FoldInCLUTs == YES) [ColorTableButton selectCellWithTag: 1]; else [ColorTableButton selectCellWithTag: 0]; if (UseVerbosePS == YES) [PSCodeSetButton selectCellWithTag: 1]; else [PSCodeSetButton selectCellWithTag: 0]; if (ConvertAllChars == YES) [ConvertAllCharsButton selectCellWithTag: 1]; else [ConvertAllCharsButton selectCellWithTag: 0]; switch (CommentOperation) { case ConvertPicComments: [PicCommentItem selectCellWithTag: PICCCONVERTTAG]; break; case DiscardPicComments: [PicCommentItem selectCellWithTag: PICCDELETETAG]; break; default: // Something is wrong. But rather than let the user know, since this // isn't a commercial quality app, we just silently select the safest option. [PicCommentItem selectCellWithTag: PICCCONVERTTAG]; break; } // // Display the panel for the user. // [prefPanel makeKeyAndOrderFront:self]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: ChangePacking: // Parameters: the object that called us // Returns: self; // Description: // This method gets called whenever the user clicks on a button to change // the setting of whether the output should be packed or not. If the button they // clicked on had a key of '1', then we will pack the image, otherwise we don't. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -ChangePacking: sender { if ( [[sender selectedCell] tag] == 1) PackImage = YES; else PackImage = NO; [self SetBoolPref: PACKIMAGE To: PackImage]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: ChangeCLUTuse: // Parameters: the object that called us // Returns: self // Description: // This method gets called whenever the user clicks on a button to change // the setting of whether we should include wrap the clut information into a // color bitmap being written out, or if we should write the clut separately. // See the bitmap processing routines for more info. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -ChangeCLUTuse: sender { if ( [[sender selectedCell] tag] == 1) FoldInCLUTs = YES; else FoldInCLUTs = NO; [self SetBoolPref: USECLUTS To: FoldInCLUTs]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: ChangePSCodeSet: // Parameters: the object that called us // Returns: self // Description: // When the user asks to change which PS code set to use, this method is // invoked. If the button had a tag of one, we assume they want to use the // 'verbose' (commented) set of PS routines when writing out. Otherwise, then // want the dense but uncommented set. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -ChangePSCodeSet: sender { if ( [[sender selectedCell] tag] == 1) UseVerbosePS = YES; else UseVerbosePS = NO; [self SetBoolPref: VERBOSEPS To: UseVerbosePS]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: ChangeCharConversion: // Parameters: the object that called us // Returns: self // Description: // Sets a flag that will govern whether we conver all, or only known type families. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -ChangeCharConversion: sender { if ( [[sender selectedCell] tag] == 1) ConvertAllChars = YES; else ConvertAllChars = NO; [self SetBoolPref: CONVERTCHARS To: ConvertAllChars]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: ChangeCommentProcessing: // Parameters: the object that called us // Returns: self // Description: // This is called, presumably, by the user's clicking on a button in the interface. // That button click indicates a desire to change what the default conversion // for PicComments should be. So, figure out which button they pressed // (e.g. convert the comments, or discard them) and set the default // accordingly. // Notes: // At present this supports 2 forms of pic comment conversion. I've tried to leave // all the constants and stuff about PicComments general enough to allow one to // add more options later (I can forsee, for instance, one that only strips out the // bulky and unneeded PS code but leaves the rotated text instructions) // History // 93.07.18 djb created. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ChangeCommentProcessing: sender { if ( [[sender selectedCell] tag] == PICCCONVERTTAG) { CommentOperation = ConvertPicComments; [self SetPref: CONVERTPICC To: CONVERTALLPICC]; } else { CommentOperation = DiscardPicComments; [self SetPref: CONVERTPICC To: DISCARDALLPICC]; } return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: openSourceFile: // Parameters: the name of the file we are to open // Returns: the new file object // Stores: error // Description: // This simply attempts to open the source file. If we are successfull, we // return it to the caller, otherwise we return an error. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - openSourceFile: (roCString) theFile { Instance fileInstance; [self ResetResults]; fileInstance = [[PICTFile alloc] initAndUse: theFile]; if ([fileInstance GetErrorCode] == ERR_OK) [fileInstance OpenExistingFor: FILE_READ]; if ([fileInstance GetErrorCode] != ERR_OK) { if ([fileInstance GetErrorCode] == ERR_UNKNOWNVERSION) [self StoreErrorCode: ERR_CANTOPEN AndText: "Aborting because that doesn't seem to be a PICT file."]; else [self StoreErrorCode: ERR_CANTOPEN AndText: "Unable to open that file for some reason."]; [fileInstance free]; fileInstance = NullInstance; } return fileInstance; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: openDestFile: // Parameters: the name of the file we are to open // Returns: the new file object // Stores: error // Description: // This simply attempts to open the destination file. If we are successfull, we // return it to the caller, otherwise we return an error. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - openDestFile: (roCString) theFile { Instance fileInstance; [self ResetResults]; fileInstance = [[PSFile alloc] initAndUse: theFile]; if ([fileInstance GetErrorCode] == ERR_OK) [fileInstance ClearAndOpenFor: FILE_WRITE]; if ([fileInstance GetErrorCode] != ERR_OK) { [self StoreErrorCode: ERR_CREATEFAILED AndText: "We were not able to create/open that file"]; [fileInstance free]; fileInstance = NullInstance; } return fileInstance; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: FilterPICT:userData:error: // Parameters: // The data to be filtered // A pointer to an error string for us to return. // Returns: self // Stores: none // Description: // Yet Another Hack (YAH(tm)). // This method can be summarized quickly: This provides the code to implement a // filter service Given pict data or the name of a pict file on the pasteboard, this will // turn it into eps data on the pasteboard and return it to the caller. By and large, the // operation is pretty straightforward. The one weird thing in all this is that in the case // of PICT data on the pasteboard, we create a temporary file and write the pasteboard // out to it. And, then, we create another temporary file to hold the results of the // conversion which, when done, we read in and return to the caller on the pasteboard. // Why not just pass pointers to chunks of RAM? Easy answer: the PICT converter only // understands the File object, and subclasses, and I've not yet written one which // uses ram as it's 'file'. So, the quick and dirty soltuion was to massage the source // data into a form that class could handle // Bugs // 93.07.18 This doesn't deal well if the 'PICT' data doesn't look like PICT data at all. // 93.07.18 At the moment, we hide the app at the start of this method, and don't // re-display it. The theory here is that a filter should be out of sight, // mostly, since it's mainly just behind-the-scenes anyway. However, // this whole routine brings up three issues. First, this whole 'architecture' // for getting set up to call the PICT Converter is stressed a bit... It feels like // there's too much repeated material scattered around, because the original // structuring (user chooses both source and destination files by way of // menus) didn't flex well with the addition of drag and drop, speaker/listener // stuff with Convert RTF, and now this. // Second, we might want to show the conversion window at some point, // because long filterings can take, well, a long time, and watching the // percent indicator would make things reassurring. For reasons I do'nt // understand, though (nor have I devoted lots of time to investigating) // that beastie isn't appearing anyway right now. // Thirdly, maybe the best solution to some of the 'hide show' issues is to not // display a window at the outset, and have it only appear when menu coices // are made (unless they need drag and drop?) // Oh, and of course, we write everything out to files so we can convert it. // I suspect we need some kinda subclass of PICT file to deal with in-ram data // History // 93.07.18 djb Created ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - FilterPICT: myPasteboard userData: (ConstCString) userData error: (CString*) msg; { char* sourceData; int sourceLength; Integer destLength; ByteString pictHeader; CString finalString; CString tempString; Instance result; Instance destFile; Instance sourceFile; NXAtom filterableTypes[5]; NXAtom sourceType; NXAtom returnTypes[2]; Integer errorValue; // // Hide the application. We're a Filter, darnit! not a gaudy user interface thingie! // [NXApp hide: self]; // // Set up array of tye pasteboard types we can deal with. // filterableTypes[0] = NXCreateFilenamePboardType("PICT"); filterableTypes[1] = NXCreateFilenamePboardType("pict"); filterableTypes[2] = NXCreateFilenamePboardType("macpict"); filterableTypes[3] = "PICT"; filterableTypes[4] = NULL; // // Find a type that we can understand from the pasteboard. // LAZY ALERT: Under some unusual circumstances, this could probably // return a null pointer. Or something. I'm not checking for this, but should be! // sourceType = [myPasteboard findAvailableTypeFrom: filterableTypes num:4 ]; // // Now that we, of course, found a data type we understand, read it in it in. // result = [myPasteboard readType: sourceType data: &sourceData length: &sourceLength ]; // // If we failed to read the data in, then return an error message, a la one of // NeXT's examples. Otherwise, move on to setting up for the filtering // if (result == nil) *msg = "Error: Could not get the data so as to filter it."; else { // // Set up the PICT file to read the data from. If the stuff was on the // pasteboard, then copy it to a temporary file. If, however, the // pasteboard was a filename, just open the specified file. // Because the PICTFile object is presently read-only (sigh), then we must // open a temporary generic file, dump the data in, close and then reopen it. // What a wonder of efficiency! // if (strcmp(sourceType, "PICT") == 0) { sourceFile = [[File alloc] initAndUseTemporary]; [sourceFile CreateAndOpenFor: FILE_WRITE]; // // Write out a bogus 512 byte header, since PICT files have to have // this. Seems like a silly way to get 512 junk bytes, byt also seems // faster than writing out 512 individual bytes or 128 longwords // pictHeader = NewByteString(512); [sourceFile Write: 512 BytesFrom: pictHeader]; FreeByteString(pictHeader); [sourceFile Write: sourceLength BytesFrom: sourceData]; tempString = [sourceFile GetPathname]; [sourceFile CloseAndSave]; [sourceFile free]; sourceFile = [[PICTFile alloc] initAndUse: tempString]; [sourceFile OpenExistingFor: FILE_READ]; FreeCString(tempString); } else // One of the various PICT file types (.macpict, .pict, or .PICT) { sourceFile = [[PICTFile alloc] initAndUse: sourceData]; [sourceFile OpenExistingFor: FILE_READ]; } // // LAZY ALERT: Naturally, there were no errors // // // Now, we can just open a temporary file for the converted data, // set up the user interface (strings in the window, and disabling // of the menu items), and call the conversion routine. // destFile = [[PSFile alloc] initAndUseTemporary]; [destFile CreateAndOpenFor: FILE_READWRITE]; [SourceFileName setStringValue: "PICT data"]; [SourceTitle setStringValue: "Filtering:"]; [SourcePathTitle setStringValue: ""]; [SourcePath setStringValue: ""]; [DestFileName setStringValue: "eps data"]; [DestTitle setStringValue: "To:"]; [DestPathTitle setStringValue: ""]; [DestPath setStringValue: ""]; [StatusTitle setStringValue: "Status:"]; [Status setStringValue: "Filtering"]; [quitCommand setEnabled: NO]; [hideCommand setEnabled: NO]; [infoCommands setEnabled: NO]; [editCommands setEnabled: NO]; [servicesCommands setEnabled: NO]; [windowsCommands setEnabled: NO]; [self ConvertFrom: sourceFile To: destFile]; errorValue = [self GetErrorCode] ; // // Restore the menu items.(isn't there a nicer way?) // [quitCommand setEnabled: YES]; [hideCommand setEnabled: YES]; [infoCommands setEnabled: YES]; [editCommands setEnabled: YES]; [servicesCommands setEnabled: YES]; [windowsCommands setEnabled: YES]; // // Do different actions, depending on whether we succeeded or not // if (errorValue == ERR_OK) { // // Well, amazing as it may seem, we seem to have succeeded in filtering the // data. Get the results back onto the pasteboard and update the gui // (just in case someone turned off the 'hide' of the app above) // destLength = [destFile FileSize]; [destFile MoveTo: 0]; finalString = NewByteString(destLength); [destFile Read: destLength BytesInto: finalString]; returnTypes[0] = NXPostScriptPboardType; [myPasteboard declareTypes: returnTypes num: 1 owner: nil]; [myPasteboard writeType: NXPostScriptPboardType data: finalString length: destLength]; FreeByteString(finalString); [SourceTitle setStringValue: "Filtered:"]; [Status setStringValue: "Filtered successfully."]; [self StoreErrorCode: ERR_OK AndText: "Filtering process done"]; } else { // // Return a pretty trivial eps file, so nothing barfs if it looks. Set an // error return string, modify the user interface. // finalString = "%!PS-Adobe-3.0 EPSF-3.0\n%%BoundingBox: 0 0 0 0\n%%EndComments"; returnTypes[0] = NXPostScriptPboardType; returnTypes[1] = NULL; [myPasteboard declareTypes: returnTypes num: 1 owner: nil]; [myPasteboard writeType: NXPostScriptPboardType data: finalString length: strlen(finalString)]; *msg = "Error: Could not filter that (was it really PICT data?)."; [SourceTitle setStringValue: "Failed:"]; tempString = [self GetErrorText]; [Status setStringValue: tempString]; FreeCString(tempString); } // // Close the destination file, and deal with the source (if we created it, // delete it. It we did not, then just close it) // [destFile CloseAndDelete]; if (strcmp(sourceType, "PICT") == 0) [sourceFile CloseAndDelete]; else [sourceFile Close]; // // Unlike the NeXT example, we're going to actually release the // data when done. // [myPasteboard deallocatePasteboardData: sourceData length: sourceLength ]; } return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: ConvertFrom:To: // Parameters: // The file to be converted from // the file to be converted to // Returns: self // Stores: error // Description: // This asks the super class to do any initalizing it needs to, and then goes // ahead and asks the converter object to go at it. // Before we do the conversion, though, we turn off the conversion menu item, // set up a string describing where to find the PS source code, and // set a couple settings in the converter instance. Once we are finished, we // re-enable the menu item, and store any error information. // Bugs // History // 93.07.18 djb Added nxping() cal. This should help to ensure that all the // gui updates of menus, etc, have been flushed to the screen.. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ConvertFrom: sourceFile To: destinationFile { CString CodeSet = NewCString(strlen(AppHome)+20); CString tempString; [self ResetResults]; [ConvertCommand setEnabled: NO]; [super ConvertFrom: sourceFile To: destinationFile]; if (UseVerbosePS == YES) sprintf(CodeSet, "%s/%s", AppHome, "CommentedPSCode/"); else sprintf(CodeSet, "%s/%s", AppHome, "UncommentedPSCode/"); [converterInst SetImagePacking: PackImage UsingPSIn: CodeSet]; [converterInst SetConvertAllChars: ConvertAllChars]; [converterInst SetPicCommentConversion: CommentOperation]; NXPing(); [converterInst ConvertPICTfile: sourceFile ToEPSfile: destinationFile]; [ConvertCommand setEnabled: YES]; FreeCString(CodeSet); if ( [converterInst GetErrorCode] < ERR_OK) { tempString = [converterInst GetErrorText]; [self StoreErrorCode: [converterInst GetErrorCode] AndText: tempString]; FreeCString(tempString); } else [self StoreErrorCode: ERR_OK AndText: "Converted Successfully" ]; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.