Defining the callbacks attached to each button is straightforward. Recall that in CAPI, because we stated that the button callback type was :interface
, the argument passed to a callback is the interface whose activation triggered that callback.
The credit-callback
is activated by the Credit button of some account
interface:
(defun credit (self)
(with-slots (balance-field account-ref) self
(let ((amount (capi:prompt-for-integer "Amount?" :min 0)))
(when amount
(op:credit account-ref amount)
(setf (capi:display-pane-text balance-field)
(format nil "~A" (op:balance account-ref)))))))
The callback is passed the account
interface. It then extracts the CORBA object reference stored in the frame's account-ref
slot and prompts the user for an amount. The function capi:prompt-for-integer
queries the user for an integer and returns nil if the user cancels the dialog. If the amount is valid, the callback invokes the stub method op:credit
on the CORBA object reference with the specified absolute value of the amount (recall that the credit
operation expects an unsigned long
as its argument). Finally, it updates the balance
field of the frame with the current value of the object's balance
attribute, obtained by invoking the stub method op:balance
.
The definition of debit-callback
is very similar to the definition of credit-callback:
(defun debit (self)
(with-slots (balance-field account-ref) self
(let ((amount (capi:prompt-for-integer "Amount?" :min 0)))
(when amount
(handler-case
(progn
(op:debit account-ref amount)
(setf (capi:display-pane-text balance-field)
(format nil "~A" (op:balance account-ref))))
(BankingDemo:account/refusal
(xx)
(capi:display-message "Debit returned refusal with
string: <~A>"
(op:reason xx))))))))
The only difference is that debit-callback
must deal with the additional possibility that the debit
operation, when invoked on the target object, may fail, raising the IDL exception refusal. If the object raises this exception, the op:debit
stub method signals it as a Common Lisp condition of class BankingDemo:account/refusal
.
The exception can then be caught and handled in any of the standard Common Lisp ways. Here, we simply place the invocation in the body of a handler-case
statement with an appropriate exception clause to handle the condition.
The open-account-callback
is activated by the openAccount-button
of some bank frame:
(defun open-account-callback (self)
(with-slots (bank-ref) self
(let ((name (capi:prompt-for-string "Name?")))
(when name
(handler-case
(let ((account-ref
(op:openaccount bank-ref name)))
(make-account-frame account-ref
:bank-interface self :title name))
(Bankingdemo:Bank/DuplicateAccount
()
(capi:display-message "Cannot create account for
~A" name)))))))
The callback extracts the CORBA object reference stored in the interface's bank-ref
slot. The function capi:prompt-for-string
queries the user for the new customer's name returning a string (or nil if the user cancels the dialog). If the dialog has not been cancelled, the callback invokes the stub method op:openAccount
on the target object reference bank, passing the argument name. If successful, the invocation returns an object reference, of class BankingDemo:account
, to an IDL account object, which is then used to make and start a new account-interface
, via a call to make-account-frame
.
Recall that the IDL operation openAccount
may fail, raising the IDL user exception duplicateAccount
. As in the definition of debit-callback
, we cater for this eventuality by placing the invocation in the body of a handler-case
statement and install a handler on the corresponding Common Lisp condition of class BankingDemo:bank/duplicateAccount
. This handler simply informs the user of the exception using the CAPI function display-message
to create and display a simple alert dialog box.
The definition of open-checking-account-callback
is similar to the definition of openAccount-callback
but prompts the user for an additional integer to pass as the overdraft limit of the new checking account:
(defun open-checking-account-callback (self)
(with-slots (bank-ref) self
(let ((name (capi:prompt-for-string "Name?")))
(when name
(let ((limit (capi:prompt-for-integer "Limit?")))
(when limit
(handler-case
(let ((account-ref
(op:opencheckingaccount bank-ref
name limit)))
(make-account-frame account-ref
:bank-interface self :title name))
(Bankingdemo:Bank/DuplicateAccount
()
(capi:display-message "Cannot create another
account for ~A" name)))))))))
While openAccount
and openCheckingAccount
create accounts for new customers, the retrieveAccount
operation is simply meant to look up the account of an existing customer:
(defun retrieve-account-callback (self)
(with-slots (bank-ref) self
(let ((name (capi:prompt-for-string "Name?")))
(when name
(if (find-named-frame self name)
(capi:display-message "Already viewing it...")
(handler-case
(let ((account-ref
(op:retrieveaccount bank-ref name)))
(when (op:Is_a account-ref (op:id
Bankingdemo:_Tc_Checkingaccount))
(setf account-ref
(op:narrow 'Bankingdemo:Checkingaccount
account-ref)))
(make-account-frame account-ref
:bank-interface self :title name))
(Bankingdemo:Bank/NonExistentAccount
()
(capi:display-message "No account exists for
name ~A" name))))))))
This callback incorporates a test that prevents the user from being presented with more than one interface to the same account. It invokes the stub method op:retrieveAccount
only if the account under that name is not already on display. Because of IDL inheritance, the server implementing the IDL retrieveAccount
operation may return any object reference whose interface inherits from the IDL account interface.
In particular, the server may return an IDL checkingAccount
as a special instance of an IDL account. In Common Lisp terms, this means that the stub method Op:retrieveAccount
may return an object reference of class BankingDemo:checkingAccount
as a special instance of BankingDemo:account
. The call to make-account-frame
dispatches on the actual, or most derived, class of the resulting object reference, making an account-interface
or checking-account-interface
as appropriate.
The definition of the close-account-callback
is straightforward:
(defun close-account-callback (self)
(with-slots (bank-ref) self
(let ((name (capi:prompt-with-list (all-frame-names self)
"Choose account")))
(when name
(op:closeaccount bank-ref
(with-slots (account-ref)
(find-named-frame self name)
account-ref))
(remove-account-frame self name)))))
The function prompt-with-list
presents a dialog asking the user to select a name from the list of available account frames (indexed by their account-name
), returning nil if the user decides to cancel the dialog. Given a valid selection, the callback invokes the stub method op:closeAccount
on the target object reference, bank-ref
, passing the name of the selected account. Finally, the account interface is removed from the bank interface.