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.