This is FlashDocument.m in view mode; [Download] [Up]
#import "FlashDocument.h"
#import "FlashApp.h"
#import "Deck.h"
#import "Card.h"
#import "Random.h"
#import <appkit/Matrix.h>
#import <appkit/ScrollView.h>
#import <appkit/Text.h>
#import <appkit/Font.h>
#import <appkit/Panel.h>
#import <appkit/ButtonCell.h>
#import <appkit/nextstd.h>
#import <objc/hashtable.h> /* for NXCopyStringBuffer() */
#import <text/pathutil.h>
#define KEY 0 // Text Views
#define RES 1
#define P_HIGH 104.0 // Cell Size
#define P_WIDE 274.0
#define P_MARG 7.0
@implementation FlashDocument
/*
* This class is used to keep track of a Flash document.
*
/* Private Functions */
#define ObjName(S) "Card_" #S
#define SetCell(I,T,S) [[[diff cellAt:0:I] setTitle:#T] setAction:@selector(S)]
#define SetDiffCell(I,N) SetCell(I,N, was ## N ## :)
static Text *getText(id me, STR name)
{
Text* tv;
tv = [[NXGetNamedObject(name, me) docView] setDelegate:me];
return tv;
}
static void alignText(Text *text, int align)
{
[[text setAlignment:align] calcLine];
}
static void writeToCard(Card *card, id* tv)
{
int i=2; // Bottom Up
NXStream *stream;
while(i--) {
stream=NXOpenMemory(NULL,0,NX_READWRITE);
[tv[i] writeRichText:stream];
[card setFace:i stream:stream];
NXCloseMemory(stream, NX_FREEBUFFER);
[card setFace:i tag:[tv[i] alignment]];
}
}
static void readFromCard(Card *card, id* tv)
{
int i=2;
NXStream *stream;
while(i--) {
stream=[card faceStream:i];
[tv[i] readRichText:stream];
NXCloseMemory(stream, NX_SAVEBUFFER);
alignText(tv[i],[card faceTag:i]);
}
}
static Text *newText(int i, int j) // Generate a TextCell of the Printout.
{
Text *text;
NXRect myRect;
NXSetRect(&myRect, i*P_WIDE, j*P_HIGH, P_WIDE, P_HIGH );
text = [Text newFrame:&myRect];
[text setMarginLeft:P_MARG right:P_MARG top:P_MARG bottom:P_MARG];
return text;
}
/* Factory methods */
+ new
/*
* Creates a new, empty, document.
* Creates a deck; creates a window for that deck;
*/
{
self = [super new];
contents = [Deck new];
[self getWindow];
[self setName:NULL andDirectory:NULL];
[self setViewMode:NO];
[self showTextOf:[self newCard]];
return self;
}
+ newFromFile:(const char *)file
/*
* Opens an existing document from the specified file.
*/
{
if (self = [super newFromFile:file]) {
[self getWindow];
[self setName:file];
[self showTextOf:[contents giveCard]];
return self;
}
return nil;
}
/* Create and Destroy Methods*/
- getWindow
/*
* set textViews and their delegates; orders the window front;
*/
{
rand = [Random new];
[contents setMarkerAtEnd];
window=[NXApp locateWindow:[NXApp loadNibSection:"Card.nib" owner:self]];
viewMode = YES;
textView[RES]=[getText(self, ObjName(ResScroll))
setFont:[NXApp defaultFont:"ResFont"]];
textView[KEY]=[getText(self, ObjName(KeyScroll))
setFont:[NXApp defaultFont:"KeyFont"]];
[window makeKeyAndOrderFront:self];
[NXApp addDocument:[self filename]];
return self;
}
- newCard
{
cardNew = YES;
cardChanged = NO;
return [[Card new] setFace:KEY tag:NX_CENTERED];
}
- free
/*
* Free all subsisting objects
*/
{
[contents freeObjects];
return [super free];
}
/* State Variables */
- view
/*
* Returns the TextView which is first responder;
* Returns nil if firstResponder is not Text.
*/
{
view = [window firstResponder];
if (! [view isKindOfGivenName:"Text"] )
view = nil;
return view;
}
- setViewMode:(BOOL)newMode
/*
* Change between viewing and updating mode
*/
{
BOOL tmp = viewMode;
viewMode = newMode;
if (viewMode) {
if ( ![self checkCurrent] ) {
viewMode = tmp;
return nil;
}
[self cover:self];
[[reveal setTitle:"Reveal"] setAction:@selector(reveal:)];
[[[reveal setAltTitle:"Cover"] setType:NX_TOGGLE] setState:0];
SetDiffCell(0,Easy);
SetDiffCell(1,Fair);
SetDiffCell(2,Hard);
} else {
[self reveal:self];
[[reveal setTitle:"Next"] setAction:@selector(next:)];
[reveal setType:NX_MOMENTARYPUSH];
SetCell(0,Add,add:);
SetCell(1,Modify,modify:);
SetCell(2,Delete,delete:);
cardNew = cardChanged = NO;
// [[window makeFirstResponder:textView[KEY]] makeKeyWindow];
}
return self;
}
- (BOOL) viewMode;
/*
* Returns the view Mode associated with this document.
*/
{
return viewMode;
}
- switchViewMode:sender
/*
* Toggle between view and update mode
*/
{
return [self setViewMode:(! viewMode)];
}
/* Card Display Methods */
- showTextOf:aCard
/*
* set to current card before displaying
*/
{
if (! aCard) {
Notify("Deck", "End of Deck. Staring over.");
aCard = [[contents setMarkerAtEnd] giveCard];
}
currentCard = aCard;
return [self showText];
}
- showText
/*
* Display card. Blacken RES if in View Mode
*/
{
if (! currentCard) currentCard = [self newCard];
if ([reveal state]) [reveal performClick:self];
readFromCard(currentCard,textView);
[markCount setIntValue:[contents marker]];
return self;
}
/* Action methods for Card View/Update */
- reveal:sender
/*
* If I ask, display RES. If Button asks, toggle.
*/
{
if ((sender == self) || ([textView[RES] backgroundGray] == NX_BLACK))
[[textView[RES] setBackgroundGray:NX_WHITE] update];
else
[self cover:self];
return self;
}
- cover:sender
/*
* Blacken RES.
*/
{
[[textView[RES] setBackgroundGray:NX_BLACK] update];
return self;
}
- wasEasy:sender
/*
* Place in Back.
*/
{
[self showTextOf:[[contents takeCard:currentCard] giveCard]];
return self;
}
- wasFair:sender
/*
* Place in front of marker.
*/
{
[contents insertObject:currentCard at:
[rand partition:[contents marker] part:2 of :2]];
[self showTextOf:[contents giveCard]];
return self;
}
- wasHard:sender;
/*
* Place in front.
*/
{
[contents insertObject:currentCard at:
[rand partition:[contents marker] part:1 of :2]];
[self showTextOf:[contents giveCard]];
return self;
}
/* Update Mode */
- (BOOL)checkCurrent
/*
* Make sure current card isn't new or modified.
* returns NO if user requests cancel.
*/
{
if (cardChanged) {
switch(NXRunAlertPanel("Update", "Card changed. Add to deck?",
"YES", "NO", "Cancel")) {
case NX_ALERTDEFAULT:
[self add:self];
break;
case NX_ALERTALTERNATE: break;
default: return NO;
}
}
if (cardNew) {
[currentCard free];
[self showTextOf:[contents removeLastObject]];
cardNew = cardChanged = NO;
}
return YES;
}
- next:sender
/*
* Show the next card. If current one has been changed, ask.
*/
{
if (! [self checkCurrent]) return nil;
[self wasEasy:sender];
cardNew = cardChanged = NO;
return self;
}
- add:sender
/*
* Add a new card to the deck.
*/
{
if (! cardNew) {
[contents takeCard:currentCard];
currentCard = [self newCard];
}
return [self modify:sender];
}
- modify:sender
/*
* Change the current Card.
*/
{
writeToCard(currentCard,textView);
[contents takeCard:currentCard];
[self showTextOf:[self newCard]];
[window setDocEdited:YES];
return self;
}
- delete:sender
/*
* Delete the current Card.
*/
{
if (NXRunAlertPanel("Delete", "Are you sure you want to delete this card?", "YES", "NO", "Cancel") != NX_ALERTDEFAULT)
return self;
[window setDocEdited:YES];
[currentCard free];
[self showTextOf:[contents giveCard]];
return self;
}
/* Action Methods for manipulating the Deck */
- previous:sender
/*
* Move back one.
*/
{
if ([self checkCurrent]) {
[contents setMarkerAt:([contents marker]+1)];
[contents insertObject:currentCard at:0];
[self showTextOf:[contents removeLastObject]];
cardNew = cardChanged = NO;
}
return self;
}
- find:sender
/*
* Find a string. Use a panel to set parameters
*/
{
id card;
/* [self next:self]; */ [self checkCurrent];
if (card = [super find:sender]) {
[contents takeCard:currentCard];
[contents cutAt:[contents indexOf:card]];
[self showTextOf:[contents giveCard]];
}
return self;
}
- doAct:(SEL)action for:(STR)caller
/*
* Pass action methods on to Deck
*/
{
if (viewMode) {
[[contents takeCard:currentCard] perform:action with:self];
return [self showTextOf:[contents giveCard]];
} else {
NotifyArg("Update", "Can't %s in update mode.",caller);
return nil;
}
}
- shuffle:sender
/*
* Shuffle the deck.
*/
{
return [self doAct:@selector(shuffle:) for:"shuffle"];
}
- sort:sender
/*
* Organize in some coherent fashion. Use a default sort on key.
*/
{
[window setDocEdited:YES];
return [self doAct:@selector(sort:) for:"sort"];
}
- swapFields:sender
/*
* Exchange Key and Response.
*/
{
return [self doAct:@selector(flipAll:) for:"swap fields"];
}
- reset:sender
{
[markCount setIntValue:[[contents setMarkerAtEnd] marker]];
return self;
}
/* Action Methods for File services */
- print:sender
/*
* Create a view displaying the entire deck, staggered. Then print it.
*/
{
int row=0;
NXRect myRect;
View *deckView;
Text *tv[2];
Deck *pdeck;
Card *pcard;
Window *holder;
pdeck = [contents copy];
[[pdeck insertObject:currentCard at:0] setMarkerAtEnd];
NXSetRect(&myRect, 0.0, 0.0, 2*P_WIDE, ([pdeck count]+1)*P_HIGH);
deckView = [[View newFrame:&myRect] setFlip:YES];
holder = [Window newContent:&myRect style:NX_PLAINSTYLE
backing: NX_NONRETAINED buttonMask:0 defer:NO];
[holder setContentView:deckView];
[deckView addSubview:[newText(1,row) setText:[self filename]] ];
while (pcard = [pdeck giveCard]) {
tv[0] = newText(0,row++);
tv[1] = newText(1,row);
readFromCard(pcard,tv);
[deckView addSubview:tv[0]];
[deckView addSubview:tv[1]];
}
[deckView printPSCode:sender];
[holder close];
return self;
}
- save:sender
/*
* Saves the file. If this document has never been saved to disk,
* then a SavePanel is put up to ask the user what file name she
* wishes to use to save the document.
*/
{
if (! [contents count]) return self;
[contents insertObject:currentCard at:0];
[super save:sender];
[contents giveCard];
return self;
}
- revertToSaved:sender
/*
* Revert the document back to what is on the disk.
*/
{
[contents insertObject:currentCard at:0];
[super revertToSaved:sender];
[contents setMarkerAtEnd];
[self showTextOf:[contents giveCard]];
return self;
}
/* Text delegate methods. */
- (BOOL)textWillChange:sender
/*
* Assigns the current text to where the cursor is.
* Doesn't allow editing in view mode.
*/
{
view = sender;
if (viewMode) {
Notify("Update", "Can only edit cards in Update Mode.");
return YES;
} else {
cardChanged = YES;
return NO;
}
}
- (BOOL)textWillEnd:sender
/*
* Nulls the current text as it resigns first responder.
*/
{
view = nil;
return NO;
}
/*
* Target/Action messages to affect the current TextView in the main window.
* This is the places where messages from the Text menu are handled.
*/
- centerText:sender
{
alignText([self view],NX_CENTERED);
return self;
}
- leftJustifyText:sender
{
alignText([self view],NX_LEFTALIGNED);
return self;
}
- rightJustifyText:sender
{
alignText([self view],NX_RIGHTALIGNED);
return self;
}
/* Outlet Connections */
- setReveal:anObject
{
reveal = anObject;
return self;
}
- setDiff:anObject
{
diff = anObject;
return self;
}
- setMarkCount:anObject
{
markCount = anObject;
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.