PersonalMoneyShell: a Presenter for PersonalMoney
The final Presenter-View pair that we need to create will be the user interface for an instance of PersonalMoney which, in fact, is the entry point to the entire application.
If you recall, it is the responsibility of a PersonalMoney instance to hold a list of all the accounts for a particular user. Each account, in turn, holds all the transactions on it. Therefore the PersonalMoney object contains a network of all the data related to a user's accounts. It would be convenient if this information could be saved to, and restored from, disk easily. Luckily, the Dolphin MVP framework provides an abstract class, DocumentShell, which implements much of the grunt work necessary for saving a model to disk and allowing it to be reloaded later. So, we'll create our presenter class PersonalMoneyShell as a subclass of DocumentShell.
DocumentShell subclass: #PersonalMoneyShell
instanceVariableNames: 'ownerPresenter accountsPresenter'
The createComponents and model: methods are pretty straightforward, as before:
"Private - Create the presenters contained by the receiver"
ownerPresenter := self add: TextPresenter new name: 'owner'.
accountsPresenter := self add: ListPresenter new name: 'accounts'.
"Set the model associated with the receiver."
super model: aPersonalMoney.
ownerPresenter model: (aPersonalMoney aspectValue: #owner).
accountsPresenter model: (aPersonalMoney accounts).
Remember to specify the defaultModel class method:
"Answer a default model to be assigned to the receiver when it
Add the accessors:
"Answer the currently selected account or nil if there is none"
"Sets the currently selected account to aPersonalAccountOrNil.
If nil if there will be no selection"
^accountsPresenter selectionOrNil: aPersonalAccountOrNil
"Answer true if there is a currently selected account in the receiver"
And now the command handlers, once again to be placed in the commands category:
"Prompt for a new account and add it to the receiver's model"
| newAccount |
newAccount := self model addAccount: PersonalAccount new.
self selectedAccountOrNil: newAccount.
"Removes the current account from the receiver's model"
self hasSelectedAccount ifTrue: [
self model removeAccount: self selectedAccountOrNil ]
"Edit the selected account"
| account index shell |
self hasSelectedAccount ifTrue: [
account := self selectedAccountOrNil.
shell := PersonalAccountShell showOn: account.
shell when: #viewClosed send: #updateAccount: to: self with: account ].
We tack on to the notification of #viewClosed here as an indication that the editing of a particular account has been completed. We can use this point to update the representation of the account in our accounts list.
Tip: this is only necessary be because we chose to edit our accounts using a modeless shell window rather than a dialog. In the latter case we would implicitly know when the editing operation was complete (following the #showModal call) and the update could be done at this time. However, using a shell is sometimes more user friendly than using a dialog, the price being some extra coding complexity.
"Update aPersonalAccount in the accounts list"
| index |
index := self model accounts indexOf: aPersonalAccount.
self model accounts updateAtIndex: index
There are two additional class methods that can be overridden by subclasses of DocumentShell. These indicate what file extensions to use for the disk file representations of the model.
"Answer a default extension that will be used for files saved from
"Answer an Array of file types that can be associated with this
class of document."
^#(('Personal Money files (*.pm)' '*.pm')
('All Files (*.*)' '*.*'))
Tip: remember these are class methods.