
" A Kinda Rolodex (From Smalltalk/V Course)
  By Tom Wrensch & Gene Korienek

  Rolodex is a simple application that is modeled after
  a simple desktop rolodex. The application consists of
  two panes, one is a list pane that contains the 'indexes'
  for the rolodex cards and the other is a text pane that
  contains the card information.

  The rolodex information is kept in a file called rolodex.dta
  in the current directory.  A sample rolodex.dta is included
  to show how it works.

  To open the rolodex do:

        RolodexModel new open. "!


Object subclass: #RolodexModel
  instanceVariableNames: 
    'rolodex saveFlag indexLister cardEditor '
  classVariableNames: ''
  poolDictionaries: '' !

!RolodexModel class methods ! !


!RolodexModel methods !

accept: aString from: aDispatcher
        "Private - Text pane change message.
         Handle the user picking Save from a menu."
    | index |
    index := indexLister selection.
    index isNil ifTrue: [^self].
    (rolodex at: index) value: aString.
    saveFlag := false.
    ^true!

addCard
        "Private - Menu option selector.
         Prompt the user for a new card index and add
         it to the rolodex."
    | answer card |
    answer := Prompter
        prompt: 'Enter a new card index: '
        default: ''.
    answer isNil ifTrue: [^self].

    card := Association
        key: answer
        value: (String new: 0).

    rolodex add: card.
    indexLister restoreSelected: (rolodex indexOf: card).
    cardEditor update.
    saveFlag := false.!

card
        "Private - Text pane name message.
         Answer the string to display in the text pane or
         an empty string if no card is selected now."
    | index |
    index := indexLister selection.
    index isNil
        ifTrue: [^String new: 0]
        ifFalse: [^(rolodex at: index) value]!

closeIt
    "Handle a close message for the top pane."
| answer |
saveFlag ifFalse: [
    answer := (Menu
        labelArray: #('Save changes ?' ' yes' ' no')
        lines: #(1)
        selectors: (Array with: nil with: true with: false))
            popUpAt: Cursor offset.
    answer isNil ifTrue: [^self].
    answer ifTrue: [self writeData]].
indexLister topPane dispatcher closeIt.!

indexList
        "Private - List pane name method.
         Answer the list of indexes."
    ^rolodex asOrderedCollection
        collect: [:assoc | assoc key]!

indexMenu
        "Private - List pane menu method.
         Answer a menu object for the list pane."
    ^Menu
        labels: 'Add\Save\Remove' withCrs
        lines: #()
        selectors: #(addCard writeData removeCard)!

makeCardPane
        "Private - Create a text pane to handle card info input."
    ^TextPane new
        model: self;
        name: #card;
        change: #accept:from:;
        framingRatio: (0 @ (1/2) corner: 1@1);
        yourself!

makeIndexPane
        "Private - Create and answer a list pane to handle
         card indexes."
    ^ListPane new
        model: self;
        name: #indexList;
        change: #select:;
        menu: #indexMenu;
        returnIndex: true;
        framingRatio: (0 @ 0 corner: 1 @ (1/2));
        yourself!

makeTopPane
        "Private - Answer the top pane for the application"
    ^TopPane new
        model: self;
        label: 'Rolodex';
        yourself!

open
        "Open up the rolodex.  The rolodex is made up
         of a sorted collection of associations where
         the key of the association is the 'index' of
         the rolodex card and the value is the text
         of the card itself.  The rolodex is stored in
         the file rolodex.dta."

    | aTopPane |

    self readData.

    aTopPane := self makeTopPane.
    indexLister := self makeIndexPane.
    cardEditor := self makeCardPane.

    aTopPane
        addSubpane: indexLister;
        addSubpane: cardEditor.

    aTopPane dispatcher open scheduleWindow!

readCardFrom: stream
    "Private - Read the next card in the stream
     and answer it as an association, or answer
     nil if there are no more cards."
| card |
stream atEnd ifTrue: [^nil].
card := Association
    key: (stream nextLine)
    value: (stream upTo: 0 asCharacter).
^card!

readData
    "Private - Read the rolodex from the file
     rolodex.dta."
| stream card |
rolodex := SortedCollection new.
stream := File pathName: 'rolodex.dta'.
card := self readCardFrom: stream.
[card isNil] whileFalse: [
    rolodex add: card.
    card := self readCardFrom: stream].
stream close.
saveFlag := true.!

removeCard
        "Private - List pane menu selector.
         Remove the currently selected card from the
         rolodex."
    | index |
    index := indexLister selection.
    index isNil ifTrue: [^self].
    rolodex remove: (rolodex at: index).
    indexLister update.
    cardEditor update.
    saveFlag := false.!

select: anIndex
        "Private - List pane change method.
         Handle a select on a rolodex card index."
    cardEditor update!

writeCard: card to: stream
    "Private - Write the card to the given stream.
     Assume the card is an association.  Zero
     terminate the card as a seperator."
stream nextPutAll: card key.
stream cr.
stream nextPutAll: card value.
stream nextPut: 0 asCharacter.!

writeData
    "Private - Write my data to the file rolodex.dta"
| stream |
(File pathName: 'rolodex.dta') close.
File
    rename: 'rolodex.dta'
    to: 'rolodex.old'.
stream := File pathName: 'rolodex.dta'.
rolodex do: [:card |
    self writeCard: card to: stream].
stream close.
File remove: 'rolodex.old'.
saveFlag := true! !
