This is FlashDoc.m in view mode; [Download] [Up]
#import "FlashDoc.h"
/* -------------------------------------------------------------------
enum {KEY, RES, NTextViews};
RDR, Inc.
Objective-C source file for the class FlashDoc
COPYRIGHT (C), 1991, RDR, Inc.
ALL RIGHTS RESERVED.
Responsible: Approved:
RDR:Ernest Prabhakar
Date: Rev:
1991-Oct-04 ___
FlashDoc
1. Introduction
Implements a Deck of FlashCards on top of the Ordered List.
Starts at the end of the deck and works its way down.
Difficult words reappear more often.
2. Revision History
___ The starting point. Ernest Prabhakar/1991-Oct-04
3. Source Code
------------------------------------------------------------------- */
/* ------------------------- Import files ------------------------- */
#import <appkit/nextstd.h>
#import <objc/hashtable.h>
#import <appkit/Matrix.h>
#import <appkit/ScrollView.h>
#import <appkit/NXSplitView.h>
#import <appkit/Text.h>
#import <appkit/Font.h>
#import <appkit/Panel.h>
#import <appkit/ButtonCell.h>
/* ------------------------ Classes used ------------------------ */
#import <objc/List.h>
#import <appkit/Panel.h>
#import "BasicApp.h"
/* -------------------------- Defines --------------------------- */
#define ZAlloc(CL) [CL allocFromZone:[self zone]]
#define ClassKind(Obj,Class) [Obj isKindOf:[Class class]]
#define P_HIGH 104.0 // Cell Size
#define P_WIDE 274.0
#define P_MARG 7.0
#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 ## :)
/* ------------------------- Constants -------------------------- */
/* ---------------------- Class variables ----------------------- */
/* -------------------- Auxiliary Functions --------------------- */
static id getScroll(NXRect *temp, id* tv, const char *font)
{
id scroll = [[ScrollView alloc] initFrame:temp];
*tv = [[Text allocFromZone:[scroll zone]] initFrame:temp];
[[*tv setMonoFont:NO] setFont:[NXApp defaultFont:font]];
[scroll setDocView:*tv];
[[[scroll setBorderType:NX_BEZEL]
setVertScrollerRequired:YES]
setBackgroundGray:NX_WHITE];
return scroll;
}
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 setStringValueFrom:stream at:i];
NXCloseMemory(stream, NX_FREEBUFFER);
[card setIntValue:[tv[i] alignment] at:i];
}
}
static void setText(id text, NXStream *stream, int align)
{
if (stream) {
[text readRichText:stream];
NXCloseMemory(stream, NX_SAVEBUFFER);
alignText(text, align);
} else {
[text setText:"<Blank>"];
}
}
static void readFromCard(Card *card, id* tv)
{
int i=2;
while(i--)
setText(tv[i],[card stringStreamAt:i],[card intValueAt:i]);
}
static Text *newText(int half)
{
Text *text;
NXRect myRect;
NXSetRect(&myRect, half*P_WIDE, 0.0, P_WIDE, P_HIGH );
text = [[[Text alloc] initFrame:&myRect] setVertResizable:YES];
[text setMarginLeft:P_MARG right:P_MARG top:P_MARG bottom:P_MARG];
[text setMonoFont:NO];
return text;
}
static id wrapViews(id aText, id bText, NXCoord *height)
{
static NXRect myFrame;
NXRect aFrame, bFrame;
[[aText sizeToFit] calcLine];
[aText getFrame:&aFrame];
[[bText sizeToFit] calcLine];
[bText getFrame:&bFrame];
*height = MAX(NX_HEIGHT(&aFrame), NX_HEIGHT(&bFrame));
NXSetRect(&myFrame, 0.0, 0.0, 2*P_WIDE, *height);
return [[[[View allocFromZone:[aText zone]] initFrame:&myFrame]
addSubview:aText] addSubview:bText];
}
/*====================================================================
Implementation of class FlashDoc
====================================================================*/
@implementation FlashDoc : Document
{
BOOL viewMode;
BOOL cardChanged;
id textView[NTextViews];
id rand;
id reveal;
id diff;
id markCount;
unsigned int current;
}
/*====================================================================
Initialize Methods
====================================================================*/
/*--------------------------------------------------------------------
|-getWindow|
* set textViews and their delegates; orders the window front;
Returns self.
Ernest Prabhakar/1991-Oct-07
--------------------------------------------------------------------*/
-getWindow
{
static const char * const defaultName[] = {"KeyFont", "NXFont"};
static NXRect SplitRect;
int kr;
[self getWindowFromNib:"Card.nib"];
rand = [Random new];
current = [contents count];
viewMode = YES;
if (!current) [self switchViewMode:self];
[view setDelegate:self];
[view getBounds:&SplitRect];
NX_HEIGHT(&SplitRect) /= 3;
for(kr=0; kr < NTextViews; kr++) {
id scroll = getScroll(&SplitRect, &textView[kr], defaultName[kr]);
[view addSubview:scroll];
[textView[kr] setDelegate:self];
}
[[view adjustSubviews] update];
view = nil;
[self nextCard];
return self;
}
/*====================================================================
Provide Card
====================================================================*/
/*--------------------------------------------------------------------
|-nextCard|
Go to next card. If at end, wrap or generate a new blank card.
Does NOT handle synchronization.
Returns self.
Ernest Prabhakar/1991-Oct-07
--------------------------------------------------------------------*/
-nextCard
{
cardChanged = NO;
if (!current) {
if (!viewMode)
[self addCard];
else
Notify("Deck", "End of Deck. Starting over.");
current = [contents count];
}
--current;
return [self showCard];
}
/*--------------------------------------------------------------------
|-addCard|
Returns self.
Ernest Prabhakar/1991-Oct-07
--------------------------------------------------------------------*/
-addCard
{
id card = [[ZAlloc(Card) initCount:2] setIntValue:NX_CENTERED at:KEY];
int i = NTextViews;
while (i--)
[card setStringValue:" " at:i];
cardChanged = YES;
[contents addObject:card];
Notify("Deck", "New Card added at beginning of Deck.");
return self;
}
/*--------------------------------------------------------------------
|-(BOOL)sync|
Make sure what is on the screen is the same as what is on the card.
Sync as needed.
YES if not changed or synchronized, NO if aborted.
Ernest Prabhakar/1991-Oct-07
--------------------------------------------------------------------*/
-(BOOL)sync
{
if (cardChanged) {
switch(NXRunAlertPanel("Edit", "Card modified. Record changes?",
"OK", "Discard Changes", NULL)) {
case NX_ALERTALTERNATE:
return NO;
default:
[self saveCard];
return YES;
}
}
return YES;
}
/*====================================================================
Card Display Methods
====================================================================*/
/*--------------------------------------------------------------------
|-showCard|
* Display card. Blacken RES if in View Mode
Returns self.
Ernest Prabhakar/1991-Oct-07
--------------------------------------------------------------------*/
-showCard
{
if ([reveal state] && viewMode) [reveal performClick:self];
readFromCard([contents objectAt:current], textView);
[markCount setIntValue:current];
[window makeFirstResponder:window];
[[window contentView] update];
return self;
}
/*--------------------------------------------------------------------
|-saveCard|
Replace current with the contents of the view.
Returns self.
Ernest Prabhakar/1991-Oct-07
--------------------------------------------------------------------*/
-saveCard
{
writeToCard([contents objectAt:current],textView);
cardChanged = NO;
[window setDocEdited:YES];
return self;
}
/*====================================================================
State Variables
====================================================================*/
/*--------------------------------------------------------------------
|-view|
* Returns the TextView which is first responder;
* Returns nil if firstResponder is not Text.
Returns self.
Ernest Prabhakar/1991-Oct-07
--------------------------------------------------------------------*/
-view
{
view = [window firstResponder];
if (!ClassKind(view, [Text class]))
view = nil;
return view;
}
/*--------------------------------------------------------------------
|-(BOOL) viewMode;|
* Returns the viewMode associated with this document.
Ernest Prabhakar/1991-Oct-07
--------------------------------------------------------------------*/
-(BOOL) viewMode;
{
return viewMode;
}
/*--------------------------------------------------------------------
|-setViewMode:(BOOL)newMode|
* Change between viewing and editing mode
Returns nil if can't change, otherwise self.
Ernest Prabhakar/1991-Oct-07
--------------------------------------------------------------------*/
-setViewMode:(BOOL)newMode
{
viewMode = newMode;
[self sync];
if (viewMode) {
[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,Revert,revertCard:);
SetCell(1,Modify,modify:);
SetCell(2,Delete,delete:);
// [[window makeFirstResponder:textView[KEY]] makeKeyWindow];
}
return self;
}
/*--------------------------------------------------------------------
|-switchViewMode:sender|
* Toggle between view and edit mode
Returns self.
Ernest Prabhakar/1991-Oct-07
--------------------------------------------------------------------*/
-switchViewMode:sender
{
return [self setViewMode:(! viewMode)];
}
/*====================================================================
Action Methods
====================================================================*/
/* Action methods for Card View/Edit */
/*--------------------------------------------------------------------
|-reveal:sender|
* If I ask, display RES. If Button asks, toggle.
Returns self.
Ernest Prabhakar/1991-Oct-07
--------------------------------------------------------------------*/
-reveal:sender
{
id scroll = [[textView[RES] superview] superview];
if ((sender == self) || ([scroll backgroundGray] == NX_BLACK))
[[scroll setBackgroundGray:NX_WHITE] update];
else
[self cover:self];
return self;
}
/*--------------------------------------------------------------------
|-cover:sender|
* Blacken RES.
Returns self.
Ernest Prabhakar/1991-Oct-07
--------------------------------------------------------------------*/
-cover:sender
{
id scroll = [[textView[RES] superview] superview];
[[scroll setBackgroundGray:NX_BLACK] update];
return self;
}
/*--------------------------------------------------------------------
|-wasEasy:sender|
* Place in Back.
Returns self.
Ernest Prabhakar/1991-Oct-07
--------------------------------------------------------------------*/
-wasEasy:sender
{
[self nextCard];
return self;
}
/*--------------------------------------------------------------------
|-wasFair:sender|
Make sure you will see it again.
Returns self.
Ernest Prabhakar/1991-Oct-07
--------------------------------------------------------------------*/
-wasFair:sender
{
int location = [rand randMin:0 max:current];
id card = [contents removeObjectAt:current];
[contents insertObject:card at:location];
++current;
[self nextCard];
return self;
}
/*--------------------------------------------------------------------
|-wasHard:sender|
Make sure you will see it again soon.
Returns self.
Ernest Prabhakar/1991-Oct-07
--------------------------------------------------------------------*/
-wasHard:sender
{
int location = [rand randMin:(current/2) max:current];
id card = [contents removeObjectAt:current];
[contents insertObject:card at:location];
++current;
[self nextCard];
return self;
}
/*====================================================================
Edit Mode
====================================================================*/
/*--------------------------------------------------------------------
|-next:sender|
* Show the next card. If current one has been changed, ask.
Returns self.
Ernest Prabhakar/1991-Oct-07
--------------------------------------------------------------------*/
-next:sender
{
[self sync];
[self nextCard];
return self;
}
/*--------------------------------------------------------------------
|-revertCard:sender|
Undo whatever changes have been made to this Card
Returns self.
Ernest Prabhakar/1991-Oct-07
--------------------------------------------------------------------*/
-revertCard:sender
{
readFromCard([contents objectAt:current],textView);
return self;
}
/*--------------------------------------------------------------------
|-modify:sender|
* Change the current Card.
Returns self.
Ernest Prabhakar/1991-Oct-12
--------------------------------------------------------------------*/
-modify:sender
{
[self saveCard];
return [self nextCard];
}
/*--------------------------------------------------------------------
|-delete:sender| * Delete the current Card.
Returns self.
Ernest Prabhakar/1991-Oct-12
--------------------------------------------------------------------*/
-delete:sender
{
if (NXRunAlertPanel("Delete", "Are you sure you want to delete this card?", "YES", "NO", "Cancel") != NX_ALERTDEFAULT)
return self;
[window setDocEdited:YES];
[[contents removeObjectAt:current] free];
return [self nextCard];
}
/*====================================================================
Action Methods for manipulating the Deck
====================================================================*/
/*--------------------------------------------------------------------
|-previous:sender|
* Move back one.
Returns self.
Ernest Prabhakar/1991-Oct-12
--------------------------------------------------------------------*/
-previous:sender
{
[self sync];
current += 2;
if (current > [contents count]) current = [contents count];
return [self nextCard];
}
/*--------------------------------------------------------------------
|-find:sender| * Find a string. Use a panel to set parameters
Returns self.
Ernest Prabhakar/1991-Oct-14
--------------------------------------------------------------------*/
-find:sender
{
id card = [super find:sender];
if (card) {
[self sync];
current = [contents indexOf:card] + 1;
[self nextCard];
}
return self;
}
/*--------------------------------------------------------------------
|-doAct:(SEL)action for:(STR)caller|
* Pass action methods on to Deck
Returns self.
Ernest Prabhakar/1991-Oct-12
--------------------------------------------------------------------*/
-doAct:(SEL)action for:(STR)caller
{
[self sync];
[contents perform:action];
[window setDocEdited:YES];
current = [contents count];
return [self nextCard];
}
/*--------------------------------------------------------------------
|-shuffle:sender|
* Shuffle the deck.
Returns self.
Ernest Prabhakar/1991-Oct-12
--------------------------------------------------------------------*/
-shuffle:sender
{
return [self doAct:@selector(shuffle) for:"shuffle"];
}
/*--------------------------------------------------------------------
|-sort:sender|
* Organize in some coherent fashion. Use a default sort on key.
Returns self.
Ernest Prabhakar/1991-Oct-12
--------------------------------------------------------------------*/
-sort:sender
{
[self sync];
[contents sort];
return [self doAct:@selector(reverse) for:"sort"];
}
-swapFields:sender
/*
* Exchange Key and Response.
*/
{
#ifdef SWAPPING
return [self doAct:@selector(rotate) for:"swap fields"];
#else
return nil;
#endif
}
- reset:sender
{
[self sync];
current = [contents count];
[markCount setIntValue:current];
return [self nextCard];
}
/*====================================================================
Action Methods for File services
====================================================================*/
/*--------------------------------------------------------------------
|-print:sender|
* Create a view displaying the entire deck, staggered. Then print it.
Returns self. *** NOT FULLY FUNCTIONAL ***
Ernest Prabhakar/1991-Oct-15
--------------------------------------------------------------------*/
-print:sender
{
NXRect myRect;
Window *holder;
id deckView;
id aText, bText;
NXCoord Height, height;
int count;
[self sync];
count = [contents count];
NXSetRect(&myRect, 0.0, 0.0, 2*P_WIDE, (count+1)*P_HIGH);
holder = [ZAlloc(Window) initContent:&myRect style:NX_PLAINSTYLE
backing: NX_NONRETAINED buttonMask:0 defer:NO];
deckView = [ZAlloc(NXSplitView) initFrame:&myRect];
[holder setContentView:deckView];
Height = count * [deckView dividerHeight];
/* Set up Prototype Text and Box */
bText = newText(1);
[bText setText:[self filename]];
while (count--) {
id card = [contents objectAt:count];
aText = newText(0);
setText(aText, [card stringStreamAt:0], [card intValueAt:0]);
[deckView addSubview:wrapViews(aText, bText, &height)];
Height += height;
bText = newText(1);
setText(bText, [card stringStreamAt:1], [card intValueAt:1]);
}
aText = newText(0);
[aText setText:[self filename]];
[deckView addSubview:wrapViews(aText, bText, &height)];
Height += height;
NXSetRect(&myRect, 0.0, 0.0, 2*P_WIDE, Height);
[[deckView setFrame:&myRect] printPSCode:sender];
[holder close];
return self;
}
/*====================================================================
Text delegate methods.
====================================================================*/
/*--------------------------------------------------------------------
|-(BOOL)textWillChange:sender| Doesn't allow editing in view mode.
Returns self.
Ernest Prabhakar/1991-Oct-14
--------------------------------------------------------------------*/
-(BOOL)textWillChange:sender
{
if (viewMode) {
Notify("Edit", "Can only edit cards in Edit Mode.");
return YES;
} else {
cardChanged = YES;
return NO;
}
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.