source: release/1.5/source/contrib/krueger/InterfaceProjects/Utilities/lisp-controller.lisp

Last change on this file was 13646, checked in by R. Matthew Emerson, 15 years ago

Merge r13631, r13636 from trunk. (Paul Krueger's updated InterfaceProjects
contrib; fix for ticket:652)

File size: 52.1 KB
Line 
1;; lisp-controller.lisp
2#|
3The MIT license.
4
5Copyright (c) 2010 Paul L. Krueger
6
7Permission is hereby granted, free of charge, to any person obtaining a copy of this software
8and associated documentation files (the "Software"), to deal in the Software without restriction,
9including without limitation the rights to use, copy, modify, merge, publish, distribute,
10sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all copies or substantial
14portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
17LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22|#
23
24;; Implements a class that can be used within IB as a content object
25;; for various standard view types
26
27(eval-when (:compile-toplevel :load-toplevel :execute)
28 (require :ns-string-utils)
29 (require :ns-object-utils)
30 (require :nslog-utils)
31 (require :assoc-array)
32 (require :list-utils))
33
34(defpackage :lisp-controller
35 (:nicknames :lc)
36 (:use :ccl :common-lisp :iu)
37 (:export
38 added-func
39 add-child-func
40 children-func
41 content-class
42 count-func
43 delete-func
44 edited-func
45 gen-root
46 lisp-controller
47 lisp-controller-changed
48 objects
49 reader-func
50 removed-func
51 root
52 root-type
53 select-func
54 writer-func))
55
56(in-package :lc)
57
58;; lisp-controller functionality
59;; Class and methods used to view a lisp object using a NSTableView
60;; or NSOutlineView or NSBrowser.
61;; The objects slot should initialized as an object to be represented
62;; in an associated view table. Unless user-specified reader/writer
63;; functions are also specified (as described later), that object
64;; must be a sequence of objects (e.g. a list or vector) or a
65;; hast-table which is treated as a sequence of (key . value) pairs.
66;; For use with non-hierarchical display views (NSTableView)
67;; the objects will be displayed one per row in the table.
68;; For use with hierarchical display views (NSOutlineView or
69;; NSBrowser) the objects comprise the set of root objects; each
70;; of which may contain children that will be displayed. Children
71;; may themselves have children, etc.
72;; Objects can be an arbitrary lisp object (e.g. a list,
73;; vector, structure, hash-table, or class instance).
74
75;; In the View specification, table column identifiers can be
76;; set to provide information about how to access the data to be
77;; displayed in that column from the object that represents a
78;; given row.
79
80;; The user has three options for how to convert column identifiers
81;; into accessor functions (reader and also writer if the table is
82;; editable). The choice is determined by what is found in the column
83;; identifier at initialization time and by whether reader/writer
84;; functions have been specified.
85
86;; Option 1: Use the column identifier as an index
87;; If the column identifier is a number, then it is treated as
88;; a numerical index that will be used to access the row-object as
89;; a sequence (i.e. as a list or vector)
90;; with elements that correspond to numerical values set as
91;; column identifiers in IB. For example, a table-column with
92;; identifier set to "1" would access the second element of
93;; the row. This is the equivalent of:
94;; (elt row-object identifier-value)
95;; Also the writer function for such a table identifier
96;; is assumed to result in the equivalent of:
97;; (setf (elt row-object identifier-value) new-value)
98;; where as before the identifier-value is derived from the
99;; identifier-string which is a number.
100
101;; Option 2: Use the column identifier as a key specifier
102;; If the column identifier consists of a single symbol,
103;; it is treated as a key to use in accessing a value from
104;; the row-object. This is the equivalent of:
105;; (funcall (symbol-function identifier-value) row-object)
106;; The identifier value should not BE a key (e.g. #'key-function)
107;; but rather should be the symbol-name for which symbol-function
108;; exists (e.g. key-function).
109;; The identifier-value may contain package specifiers for the
110;; symbol in the normal way (e.g. my-package::my-symbol).
111;; If that column permits editing, then the lisp-controller
112;; will execute the equivalent of:
113;; (setf (identifier-value row-object) new-value)
114;; So, for example, if the row-object happens to be a list,
115;; the user could specify a key of "first" (quotes not included),
116;; since both (first row-object) and (setf (first row-object) new-value)
117;; are well-specified for lists. Note that this would, however, be
118;; equivalent to using 0.
119;; If editing of a column is permitted, but no appropriate setf
120;; method exists for the key specifier, then although the user
121;; will apparently be able to change the value of that column in
122;; the table, no actual change will be made and the value will
123;; instantly revert back to its former value.
124;; Note that a key specifier of "identity" could be used to select
125;; the row-object itself for printing. This might be suitable for
126;; single-column tables or multi-column tables where other columns
127;; display some transformation of the row-object.
128
129;; Option 3: Use the column-identifier as a symbol or number supplied
130;; as a parameter to a separately provided accessor function.
131;; If the user explicitly provides a reader function by setting the
132;; reader-function field for a lisp-controller in IB, then
133;; that function will be used for all table accesses. It will be passed
134;; three arguments: the content of the "objects" slot for the lisp-controller,
135;; the requested row number and the value of the column-identifier
136;; field for the column. If that is a number, then a numerical
137;; value is passed, if it is anything else then whatever lisp object
138;; results from doing a (read-from-string column-identifier) is passed
139;; as the second parameter. For example, this would permit the user
140;; to provide a two-dimensional array as the "objects" to be displayed,
141;; provide appropriate numerical indices as the value of various column-
142;; identifiers, and to specify "aref" as the reader function.
143;; Similarly, if a writer function is specified by the user in IB, then
144;; it will be called with four arguments: the new value, the content of
145;; the objects slot, the row-number, and the column-identifier as either
146;; a number or symbol. Using the same example, this would result in
147;; calling (setf (aref contents row-num col-identifier) new-value).
148;; Note that if a reader function is specified, then a count function
149;; should also be provided. If editing is permitted, then an "edited"
150;; function should also be provided. If row insertion and/or deletion are
151;; permitted, then appropriate "added" and "deleted" functions
152;; must also be provided.
153
154;; In addition to reader and writer functions, the user may specify
155;; functions that are called when a table entry is selected or edited.
156;; These should be functions that take 3 arguments: the content of
157;; the lisp-controller's "objects" slot, the row number,
158;; and the column-identifier-string for the affected table entry.
159;; The select function will be invoked whenever a list item is
160;; selected and the edited function will be invoked after an item
161;; in the list has been modified.
162
163;; The user may also specify functions to be called when a new row is
164;; added or deleted. These will take two arguments, the content of the
165;; "objects" slot and the row number of the new object. Note that if
166;; the user has specified their own reader and writer functions that
167;; the lisp-controller will have no way of adding or deleting objects.
168;; In this case only, the user supplied "added" and "deleted" functions
169;; should actually make the necessary changes to the first argument (i.e.
170;; add or delete as appropriate). The table will be re-displayed after
171;; this has been done.
172
173;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
174;;; Global variables
175
176(defvar *lisp-controller-debug* nil)
177
178;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
179;; class and methods specific to hash-tables
180
181(defclass ht-entry ()
182 ((ht-key :reader ht-key :initarg :key)
183 (ht-value :reader ht-value :initarg :value)
184 (ht :accessor ht :initarg :ht))
185 (:default-initargs
186 :key (gensym "KEY")
187 :value nil
188 :ht nil))
189
190(defmethod (setf ht-key) (new-key (self ht-entry))
191 (block set-it
192 (let ((new-key-exists (not (eq :not-found (gethash new-key (ht self) :not-found)))))
193 (when new-key-exists
194 ;; They are redefining a key to be one that already exists in the hash-table
195 ;; first verify they want to do this
196 (let* ((alert-str (lisp-to-temp-nsstring
197 (format nil
198 "Continuing will reset value for existing key: ~s"
199 new-key)))
200 (res (#_NSRunAlertPanel #@"ALERT"
201 alert-str
202 #@"Cancel key change"
203 #@"Continue and change key"
204 (%null-ptr))))
205 (unless (eql res #$NSAlertAlternateReturn)
206 ;; they don't want to continue
207 (return-from set-it))))
208 ;; change the value for the existing key
209 (setf (gethash new-key (ht self))
210 (gethash (ht-key self) (ht self)))
211 ;; and then remove the old key that was changed both from the hash table
212 ;; and from the list of keys
213 ;; new keys are always put at the end of the list unless a sort predicate
214 ;; has been specified.
215 (remhash (ht-key self) (ht self))
216 (setf (slot-value self 'ht-key) new-key))))
217
218(defmethod (setf ht-value) (new-val (self ht-entry))
219 (setf (gethash (ht-key self) (ht self)) new-val)
220 (setf (slot-value self 'ht-value) new-val))
221
222(let ((ht-hash (make-hash-table)))
223 ;; in order to treat hash-tables as containers like lists and vectors we
224 ;; need to define a few functions which use a cache of the "children" of
225 ;; a hash-table so that we don't need to recreate the whole list every time
226 ;; a new child is added
227
228 (defmethod children ((parent hash-table))
229 (or (gethash parent ht-hash)
230 (setf (gethash parent ht-hash)
231 (let ((ht-list nil))
232 (maphash #'(lambda (key val)
233 (push (make-instance 'ht-entry
234 :key key
235 :value val
236 :ht parent)
237 ht-list))
238 parent)
239 ht-list))))
240
241 (defmethod (setf children) (new-value (parent hash-table))
242 (setf (gethash parent ht-hash) new-value))
243
244 (defmethod add-to-child-seq (parent (seq list) (thing ht-entry))
245 (with-slots (ht ht-key ht-value) thing
246 (setf (gethash ht-hash parent) (cons thing seq))
247 (setf ht parent)
248 (setf (gethash parent ht-key) ht-value)))
249
250 (defmethod delete-from-child-seq ((seq list) (thing ht-entry))
251 (with-slots (ht ht-key) thing
252 (remhash ht-key ht)
253 (delete-from-list seq thing)))
254
255) ;; end of hash-table functions within let
256
257;;; Functions to access children for other common types
258
259(defmethod children ((parent sequence))
260 ;; root objects that are lists or vectors are
261 ;; also the sequence of children
262 parent)
263
264(defmethod (setf children) (new-children (parent vector))
265 ;; root objects that are lists or vectors are
266 ;; also the sequence of children
267 (or parent new-children))
268
269(defmethod (setf children) (new-children (parent list))
270 ;; root objects that are lists or vectors are
271 ;; also the sequence of children
272 new-children)
273
274(defmethod children (parent)
275 (declare (ignore parent))
276 ;; any other unknown type of parent
277 nil)
278
279;;; Functions to add/delete items from sequences
280;;; See also corresponding methods for sequences of ht-entry items
281;;; in section of hash-table methods above
282
283(defmethod add-to-child-seq (parent (seq list) thing)
284 (declare (ignore parent))
285 (nconc seq (list thing)))
286
287(defmethod add-to-child-seq (parent (seq vector) thing)
288 (declare (ignore parent))
289 (when (array-has-fill-pointer-p seq)
290 (vector-push-extend thing seq)))
291
292(defmethod delete-from-child-seq ((seq vector) thing)
293 (let ((pos (position thing seq)))
294 (dotimes (i (- (fill-pointer seq) pos 1))
295 (setf (aref seq (+ pos i))
296 (aref seq (+ pos i 1)))))
297 (vector-pop seq))
298
299(defmethod delete-from-child-seq ((seq list) thing)
300 (delete-from-list seq thing))
301
302;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
303;; Some utility functions used later
304
305
306(defun valid-setf-for (read-form)
307 (multiple-value-bind (a b c func-form d) (get-setf-expansion read-form)
308 (declare (ignore a b c d))
309 (or (not (eq (first func-form) 'funcall))
310 ;; this must be a built-in function, so assume setf works
311 ;; otherwise make sure the function name specified is fboundp
312 (let ((func-name (second (second func-form))))
313 (and (typep func-name 'function-name) (fboundp func-name))))))
314
315(defun eval-without-errors (form)
316 (when *lisp-controller-debug*
317 (ns-log (format nil "evaling form: ~s" form)))
318 (handler-case (eval form)
319 (condition (c)
320 (when *lisp-controller-debug*
321 (format t "~%condition: ~s" c)))))
322
323(defun reader-writer-pair (typ col-val)
324 (let* ((reader-form nil)
325 (writer-form nil))
326 (cond ((null col-val)
327 ;; reader justs return the object itself
328 ;; leave the writer-form null
329 (setf reader-form 'row))
330 ((and (eq col-val :key) (eq typ 'ht-entry))
331 ;; used for the key value in a hash table
332 (setf reader-form '(ht-key row))
333 (setf writer-form '(setf (ht-key row) new-val)))
334 ((and (eq col-val :value) (eq typ 'ht-entry))
335 ;; used for the value in a hash table
336 (setf reader-form '(ht-value row))
337 (setf writer-form '(setf (ht-value row) new-val)))
338 ((eq col-val :row)
339 (setf reader-form 'row)
340 (setf writer-form '(setf row new-val)))
341 ((numberp col-val)
342 (cond ((subtypep typ 'vector)
343 (setf reader-form `(aref row ,col-val))
344 (setf writer-form `(setf (aref row ,col-val) new-val)))
345 ((subtypep typ 'list)
346 (setf reader-form `(nth ,col-val row))
347 (setf writer-form `(setf (nth ,col-val row) new-val)))
348 ((eq typ 'ht-entry)
349 ;; Index if the value is a sequence
350 (setf reader-form `(when (typep (ht-value row) 'sequence)
351 (elt (ht-value row) ,col-val)))
352 (setf writer-form `(when (typep (ht-value row) 'sequence)
353 (setf (elt (ht-value row) ,col-val) new-val))))
354 ((subtypep typ 'hash-table)
355 ;; use the number as a key into the hash table and return the value
356 (setf reader-form `(gethash ,col-val row))
357 (setf writer-form `(setf (gethash ,col-val row) new-val)))
358 (t
359 ;; index if row is any other type of sequence
360 (setf reader-form `(when (typep row 'sequence)
361 (elt row ,col-val)))
362 (setf writer-form `(when (typep row 'sequence)
363 (setf (elt row ,col-val) new-val))))))
364 ((and (symbolp col-val) (fboundp col-val))
365 (cond ((eq typ 'ht-entry)
366 ;; Assume the function applies to the value
367 (setf reader-form `(,col-val (ht-value row)))
368 (when (valid-setf-for reader-form)
369 (setf writer-form `(setf (,col-val (ht-value row)) new-val))))
370 (t
371 (setf reader-form `(,col-val row))
372 (when (valid-setf-for reader-form)
373 (setf writer-form `(setf (,col-val row) new-val))))))
374 ((symbolp col-val)
375 (cond ((subtypep typ 'hash-table)
376 ;; use the symbol as a key into the hash table and return the value
377 (setf reader-form `(gethash ,col-val row))
378 (setf writer-form `(setf (gethash ,col-val row) new-val)))))
379 ((and (consp col-val) (eq (first col-val) 'function))
380 (let ((col-val (second col-val)))
381 (when (and (symbolp col-val) (fboundp col-val))
382 (cond ((eq typ 'ht-entry)
383 ;; Assume the function applies to the value
384 (setf reader-form `(,col-val (ht-value row)))
385 (when (valid-setf-for reader-form)
386 (setf writer-form `(setf (,col-val (ht-value row)) new-val))))
387 (t
388 (setf reader-form `(,col-val row))
389 (when (valid-setf-for reader-form)
390 (setf writer-form `(setf (,col-val row) new-val))))))))
391 ((consp col-val)
392 ;; accessors are lisp forms possibly using keywords :row, :key, and :value
393 ;; which are replaced appropriately
394 (setf reader-form (nsubst 'row ':row
395 (nsubst '(ht-key row) :key
396 (nsubst '(ht-value row) :value
397 col-val))))
398 (when (valid-setf-for reader-form)
399 (setf writer-form `(setf ,col-val new-val)))))
400 (when *lisp-controller-debug*
401 (ns-log (format nil "Reader-form: ~s~%Writer-form: ~s" reader-form writer-form)))
402 (cons (and reader-form
403 (eval-without-errors `(function (lambda (row) ,reader-form))))
404 (and writer-form
405 (eval-without-errors `(function (lambda (new-val row) ,writer-form)))))))
406
407;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
408;; lisp-controller class
409
410(defclass lisp-controller (ns:ns-object)
411 ((root :accessor root)
412 (root-type :accessor root-type)
413 (gen-root :accessor gen-root)
414 (objects :accessor objects)
415 (types :reader types)
416 (reader-func :accessor reader-func)
417 (writer-func :accessor writer-func)
418 (count-func :accessor count-func)
419 (select-func :accessor select-func)
420 (edited-func :accessor edited-func)
421 (added-func :accessor added-func)
422 (removed-func :accessor removed-func)
423 (delete-func :accessor delete-func)
424 (add-child-func :accessor add-child-func)
425 (children-func :accessor children-func)
426 (type-info :accessor type-info)
427 (obj-wrappers :accessor obj-wrappers)
428 (column-info :accessor column-info)
429 (nib-initialized :accessor nib-initialized)
430 (view-class :accessor view-class)
431 (can-remove :foreign-type #>BOOL :accessor can-remove)
432 (can-insert :foreign-type #>BOOL :accessor can-insert)
433 (can-add-child :foreign-type #>BOOL :accessor can-add-child)
434 (owner :foreign-type :id :accessor owner)
435 (view :foreign-type :id :accessor view))
436 (:metaclass ns:+ns-object))
437
438;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
439;; instance initialization methods
440
441(objc:defmethod (#/initWithCoder: :id)
442 ((self lisp-controller) (decoder :id))
443 ;; This method is called when the Nib is loaded and provides values defined
444 ;; when the NIB was created
445 (#/init self)
446 (with-slots (reader-func writer-func count-func select-func edited-func
447 add-child-func root-type gen-root added-func removed-func
448 children-func type-info delete-func) self
449 (let ((type-info-array (#/decodeObjectForKey: decoder #@"typeInfo")))
450 (dotimes (i (#/count type-info-array))
451 ;; for each type specified in IB by the user
452 (let* ((row-array (#/objectAtIndex: type-info-array i))
453 (ns-str-type-name (#/objectAtIndex: row-array 0))
454 (type-name (nsstring-to-sym ns-str-type-name))
455 (child-type (nsstring-to-sym (#/objectAtIndex: row-array 1)))
456 (child-func-str (ns-to-lisp-string (#/objectAtIndex: row-array 2)))
457 (child-func (find-func child-func-str))
458 (reader-sym (and child-func (function-name child-func)))
459 (writer-form `(setf (,reader-sym thing) new-val))
460 (child-writer-func (and child-func
461 (valid-setf-for writer-form)
462 (eval `(function (lambda (new-val thing)
463 ,writer-form))))))
464 (when child-type
465 (setf (assoc-aref type-info type-name :child-type) child-type))
466 (when child-func
467 (setf (assoc-aref type-info type-name :child-key) child-func))
468 (when child-writer-func
469 (setf (assoc-aref type-info type-name :child-setf-key) child-writer-func)))))
470 (let ((initform-array (#/decodeObjectForKey: decoder #@"initforms")))
471 (dotimes (i (#/count initform-array))
472 ;; for each initform specified in IB by the user
473 (let* ((row-array (#/objectAtIndex: initform-array i))
474 (ns-str-type-name (#/objectAtIndex: row-array 0))
475 (type-name (nsstring-to-sym ns-str-type-name))
476 (initform (ns-to-lisp-object t (#/objectAtIndex: row-array 1))))
477 (when initform
478 (setf (assoc-aref type-info type-name :initform) initform)))))
479 (let ((sort-info-array (#/decodeObjectForKey: decoder #@"sortInfo")))
480 (dotimes (i (#/count sort-info-array))
481 ;; for each sort predicate and key specified in IB by the user
482 (let* ((row-array (#/objectAtIndex: sort-info-array i))
483 (ns-str-type-name (#/objectAtIndex: row-array 0))
484 (type-name (nsstring-to-sym ns-str-type-name))
485 (sort-key (nsstring-to-func (#/objectAtIndex: row-array 1)))
486 (sort-pred (nsstring-to-func (#/objectAtIndex: row-array 2))))
487 (when sort-pred
488 (setf (assoc-aref type-info type-name :sort-pred) sort-pred))
489 (when sort-key
490 (setf (assoc-aref type-info type-name :sort-key) sort-key)))))
491 (setf root-type (nsstring-to-sym (#/decodeObjectForKey: decoder #@"rootType")))
492 (setf (types self) (delete-duplicates (list* root-type
493 'ht-entry
494 (mapcar-assoc-array #'identity type-info))))
495 (setf reader-func (nsstring-to-func (#/decodeObjectForKey: decoder #@"readerFunc")))
496 (setf writer-func (nsstring-to-func (#/decodeObjectForKey: decoder #@"writerFunc")))
497 (setf count-func (nsstring-to-func (#/decodeObjectForKey: decoder #@"countFunc")))
498 (setf select-func (nsstring-to-func (#/decodeObjectForKey: decoder #@"selectFunc")))
499 (setf edited-func (nsstring-to-func (#/decodeObjectForKey: decoder #@"editedFunc")))
500 (setf added-func (nsstring-to-func (#/decodeObjectForKey: decoder #@"addedFunc")))
501 (setf removed-func (nsstring-to-func (#/decodeObjectForKey: decoder #@"removedFunc")))
502 (setf delete-func (nsstring-to-func (#/decodeObjectForKey: decoder #@"deleteFunc")))
503 (setf add-child-func (nsstring-to-func (#/decodeObjectForKey: decoder #@"addChildFunc")))
504 (setf children-func (nsstring-to-func (#/decodeObjectForKey: decoder #@"childrenFunc")))
505 (setf gen-root (#/decodeBoolForKey: decoder #@"genRoot")))
506 self)
507
508(objc:defmethod (#/init :id)
509 ((self lisp-controller))
510 ;; need to do this to initialize default values that are needed when
511 ;; this object is instantiated from Objective-C runtime
512 (unless (slot-boundp self 'nib-initialized)
513 (setf (nib-initialized self) nil))
514 (unless (slot-boundp self 'select-func)
515 (setf (select-func self) nil))
516 (unless (slot-boundp self 'edited-func)
517 (setf (edited-func self) nil))
518 (unless (slot-boundp self 'added-func)
519 (setf (added-func self) nil))
520 (unless (slot-boundp self 'removed-func)
521 (setf (removed-func self) nil))
522 (unless (slot-boundp self 'add-child-func)
523 (setf (add-child-func self) nil))
524 (unless (slot-boundp self 'delete-func)
525 (setf (delete-func self) nil))
526 (unless (slot-boundp self 'reader-func)
527 (setf (reader-func self) nil))
528 (unless (slot-boundp self 'writer-func)
529 (setf (writer-func self) nil))
530 (unless (slot-boundp self 'count-func)
531 (setf (count-func self) nil))
532 (unless (slot-boundp self 'objects)
533 (setf (objects self) nil))
534 (unless (slot-boundp self 'root)
535 ;; note that we have to set root slot to avoid
536 ;; calling accessor functions. This is a
537 ;; special case and the only place where we
538 ;; want to set the root to something that
539 ;; doesn't match the root-type specified in IB
540 (setf (slot-value self 'root) nil))
541 (unless (slot-boundp self 'root-type)
542 (setf (root-type self) t))
543 (unless (slot-boundp self 'types)
544 (setf (types self) nil))
545 (unless (slot-boundp self 'type-info)
546 (setf (type-info self) (make-instance 'assoc-array :rank 2)))
547 ;; Now set up some default type info for standard types
548 ;; These can be overridden in the lisp-controller setup
549 ;; within Interface Builder.
550 ;; Typically users would define their own types and
551 ;; specify values for them rather than overriding these
552 ;; but it is permissable to do so.
553 (setf (assoc-aref (type-info self) 'hash-table :child-key) #'children)
554 (setf (assoc-aref (type-info self) 'list :child-key) #'children)
555 (setf (assoc-aref (type-info self) 'vector :child-key) #'children)
556 (setf (assoc-aref (type-info self) 'hash-table :child-setf-key) #'(setf children))
557 (setf (assoc-aref (type-info self) 'list :child-setf-key) #'(setf children))
558 (setf (assoc-aref (type-info self) 'vector :child-setf-key) #'(setf children))
559 (setf (assoc-aref (type-info self) 'hash-table :child-type) 'ht-entry)
560 (setf (assoc-aref (type-info self) 'list :child-type) 'list)
561 (setf (assoc-aref (type-info self) 'vector :child-type) 'vector)
562 (setf (assoc-aref (type-info self) 'hash-table :initform)
563 '(make-hash-table))
564 (setf (assoc-aref (type-info self) 'list :initform)
565 nil)
566 (setf (assoc-aref (type-info self) 'vector :initform)
567 '(make-array '(10) :adjustable t :fill-pointer 0 :initial-element nil))
568 (unless (slot-boundp self 'obj-wrappers)
569 (setf (obj-wrappers self) (make-instance 'assoc-array :rank 1)))
570 self)
571
572(objc:defmethod (#/awakeFromNib :void)
573 ((self lisp-controller))
574 (setf (nib-initialized self) t)
575 (unless (eql (view self) (%null-ptr))
576 (setf (view-class self) (#/class (view self)))
577 (init-column-info self (view self))
578 (when (gen-root self)
579 ;; create the root object
580 (setf (root self) (new-object-of-type self (root-type self))))
581 (when (objects self)
582 (setup-accessors self))))
583
584(defmethod setup-accessors ((self lisp-controller))
585 ;; This method must be called to initialize the column value
586 ;; accessor functions for a lisp-controller.
587 ;; It is called after NIB loading has been done.
588 (with-slots (reader-func column-info type-info types) self
589 (unless reader-func
590 (dolist (col (mapcar-assoc-array #'identity column-info))
591 (let ((col-id (assoc-aref column-info col :col-val)))
592 (dolist (typ types)
593 (setf (assoc-aref type-info typ col)
594 (reader-writer-pair typ col-id))))))))
595
596(defmethod set-can-add-child ((self lisp-controller) row-selected)
597 ;; indicates whether new children objects can be placed within
598 ;; the object represented in the row specified.
599 ;; If we have been given an explict add-child function then we can or
600 ;; if we know the child type for the root type and have a valid
601 ;; child key for which there is an associated setf function
602 ;; then we can also insert a new object.
603 (#/willChangeValueForKey: self #@"canAddChild")
604 (let* ((obj (object-at-row self row-selected))
605 (obj-type (controller-type-of self obj))
606 (child-setf-key (assoc-aref (type-info self) obj-type :child-setf-key)))
607 (if child-setf-key
608 (setf (can-add-child self) #$YES)
609 (setf (can-add-child self) #$NO)))
610 (#/didChangeValueForKey: self #@"canAddChild"))
611
612(defmethod set-can-insert :around ((self lisp-controller) new-obj)
613 (declare (ignore new-obj))
614 (#/willChangeValueForKey: self #@"canInsert")
615 (call-next-method)
616 (#/didChangeValueForKey: self #@"canInsert"))
617
618(defmethod set-can-insert ((self lisp-controller) new-obj)
619 (declare (ignore new-obj))
620 ;; indicates whether new children objects can be placed within
621 ;; the root object.
622 ;; If we have been given an explict insert function then we can or
623 ;; if we know the child type for the root type and have a valid
624 ;; child key for which there is an associated setf function
625 ;; then we can also insert a new object.
626 (if (or (add-child-func self)
627 (assoc-aref (type-info self) (root-type self) :child-setf-key))
628 (setf (can-insert self) #$YES)
629 (setf (can-insert self) #$NO)))
630
631(defmethod set-can-insert ((self lisp-controller) (new-obj vector))
632 (setf (can-insert self)
633 (if (or (add-child-func self)
634 (and (assoc-aref (type-info self) (controller-type-of self new-obj) :child-setf-key)
635 (array-has-fill-pointer-p (objects self))
636 (or (adjustable-array-p (objects self))
637 (< (fill-pointer (objects self))
638 (first (array-dimensions (objects self)))))))
639 #$YES
640 #$NO)))
641
642(defmethod (setf types) (new-types (self lisp-controller))
643 ;; sort the types making most specific first
644 ;; when we look for a type match using the function controller-type-of
645 ;; we'll find the most specific type that matches the given object
646 ;; Unfortunately we can't just call sort using #'subtypep as a predicate
647 ;; because it may not create a list where it is guaranteed that a appears
648 ;; before b whenever a is a subtype of b. So we do our own manual insertion
649 ;; sort.
650 (let ((res-list nil)
651 (pos nil))
652 (dolist (typ new-types)
653 (setf pos (position-if #'(lambda (check-type)
654 (subtypep typ check-type))
655 res-list))
656 (if pos
657 ;; splice type into the res-list at pos
658 (setf res-list (add-to-list-at res-list pos typ))
659 (setf res-list (nconc res-list (list typ)))))
660 (setf (slot-value self 'types) res-list)))
661
662;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
663;; View-specific methods
664
665(defmethod init-column-info ((self lisp-controller) (view ns:ns-table-view))
666 (with-slots (column-info) self
667 (let* ((tc-arr (#/tableColumns view))
668 (col-obj nil)
669 (idc nil)
670 (col-count (#/count tc-arr)))
671 (unless tc-arr
672 (ns-log "#/tableColumns returned nil for view")
673 (return-from init-column-info))
674 (setf column-info (make-instance 'assoc-array :rank 2))
675 (dotimes (i col-count)
676 (setf col-obj (#/objectAtIndex: tc-arr i))
677 (setf (assoc-aref column-info col-obj :col-indx) i)
678 (setf (assoc-aref column-info i :col-obj) col-obj)
679 (setf idc (ns-to-lisp-string (#/identifier col-obj)))
680 (setf (assoc-aref column-info col-obj :col-string) idc)
681 (setf (assoc-aref column-info col-obj :col-val)
682 (read-from-string idc nil nil))
683 (setf (assoc-aref column-info col-obj :col-title)
684 (ns-to-lisp-string (#/title (#/headerCell col-obj))))
685 ;; find any formatter attached to the data cell for this column and
686 ;; use info from it to help us translate to and from lisp objects
687 ;; appropriately
688 (let ((formatter-object (#/formatter (#/dataCell col-obj))))
689 (unless (eql formatter-object (%null-ptr))
690 (cond ((typep formatter-object 'ns:ns-date-formatter)
691 (setf (assoc-aref column-info col-obj :col-format) :date))
692 ((typep formatter-object 'ns:ns-number-formatter)
693 (cond ((#/generatesDecimalNumbers formatter-object)
694 (let ((dec-digits (#/maximumFractionDigits formatter-object)))
695 (setf (assoc-aref column-info col-obj :col-format)
696 (list :decimal dec-digits))))
697 (t
698 (setf (assoc-aref column-info col-obj :col-format)
699 :number)))))))))))
700
701
702(defmethod object-at-row ((self lisp-controller) row-selected)
703 ;; returns two objects: the lisp object represented by the specified
704 ;; row and the parent of that object
705 (unless (eql row-selected -1)
706 (cond ((eql (view-class self) ns:ns-outline-view)
707 (let ((ptr-wrapper (#/itemAtRow: (view self) row-selected)))
708 (values (lpw-lisp-ptr ptr-wrapper)
709 (lpw-parent ptr-wrapper))))
710 ((eql (view-class self) ns:ns-table-view)
711 (values (elt (objects self) row-selected) (root self))))))
712
713;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
714;; Utility methods for the lisp-controller class
715
716(defmethod controller-type-of ((self lisp-controller) thing)
717 ;; find which of the declared types (if any) matches thing
718 ;; if none do, just return its lisp type
719 (if (eq thing (root self))
720 (root-type self)
721 (or (find-if #'(lambda (typ)
722 (typep thing typ))
723 (types self))
724 (type-of thing))))
725
726(defmethod controller-types-of ((self lisp-controller) thing)
727 ;; finds all of the declared types (if any) that match thing
728 ;; in an order from most specific to least specific including
729 ;; its lisp type
730 (nconc (remove-if-not #'(lambda (typ)
731 (typep thing typ))
732 (types self))
733 (list (type-of thing))))
734
735(defmethod most-specific-type-info ((self lisp-controller) type-list info-key)
736 (some #'(lambda (typ)
737 (assoc-aref (type-info self) typ info-key))
738 type-list))
739
740;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
741;; methods callable by outside functions within a lisp program
742
743(defmethod lisp-controller-changed ((self lisp-controller))
744 ;; program should call this if it changes the contents of the
745 ;; list object (but not the pointer to the list itself). In
746 ;; the latter case set-lisp-list-controller should be called.
747 (#/reloadData (view self)))
748
749(defmethod (setf root) :before (new-obj (self lisp-controller))
750 (let ((typ (controller-type-of self new-obj))
751 (rt (root-type self)))
752 (when (not (subtypep typ rt))
753 ;; trying to set root to something other than what was specified in IB
754 (error "Type of ~s (~s) is not a subtype of ~s" new-obj typ rt))))
755
756(defmethod (setf root) :after (new-obj (self lisp-controller))
757 ;; cache the children of the root object because they are used so frequently
758 (setf (objects self) (children-of-object self new-obj))
759 (when (nib-initialized self)
760 (setup-accessors self)
761 (set-can-insert self new-obj)
762 (sort-sequence self (objects self))
763 (#/reloadData (view self)))
764 new-obj)
765
766(defmethod (setf view) :after (new-view (self lisp-controller))
767 ;; only used if lisp-list-controller object is not created via a nib load
768 ;; and view is set from somewhere else
769 (when new-view
770 (init-column-info self (view self))))
771
772;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
773;; miscellaneous methods for accessing and transforming objects
774
775(defmethod new-child-initform ((self lisp-controller) parent-type)
776 ;; get the initform for the child type of the parent-type parameter
777 (let ((child-type (assoc-aref (type-info self) parent-type :child-type)))
778 (when child-type
779 ;; child-type cannot be nil unless there are no children
780 (assoc-aref (type-info self) child-type :initform))))
781
782(defmethod new-object-of-type ((self lisp-controller) typ)
783 ;; Create & initialize a new instance of some type
784 (eval (assoc-aref (type-info self) typ :initform)))
785
786(defmethod children-of-object ((self lisp-controller) (obj ht-entry))
787 ;; We want to handle the children of ht-entry objects a bit differently
788 ;; Basically we would like to return the children of the value of the entry
789 (children-of-object self (ht-value obj)))
790
791(defmethod children-of-object ((self lisp-controller) obj)
792 ;; Get the children of an instance of some type
793 (let* ((obj-type (controller-type-of self obj))
794 (child-key (assoc-aref (type-info self) obj-type :child-key))
795 (children-object nil))
796 (if (children-func self)
797 (setf children-object (funcall (children-func self) (owner self) self obj))
798 (if child-key
799 (setf children-object (funcall child-key obj))))
800 ;; if the children object is a hash-table, expand it into an ht-entry list
801 (when (typep children-object 'hash-table)
802 (setf children-object (children children-object)))
803 (sort-sequence self children-object)))
804
805(defmethod add-child-to ((self lisp-controller) (parent ht-entry))
806 ;; We want to handle the children of ht-entry objects a bit differently
807 ;; Basically we would like to add a child to the children of the value of the entry
808 (add-child-to self (ht-value parent)))
809
810(defmethod add-child-to ((self lisp-controller) parent)
811 (let* ((parent-type (controller-type-of self parent))
812 (child-type (assoc-aref (type-info self) parent-type :child-type))
813 (child-key (assoc-aref (type-info self) parent-type :child-key))
814 (child-initform (and child-type (assoc-aref (type-info self) child-type :initform)))
815 (set-child-func (assoc-aref (type-info self) parent-type :child-setf-key))
816 (new-children nil)
817 (new-child nil))
818 (if (and child-type child-key child-initform set-child-func)
819 ;; we've got everything we need to set the child ourselves
820 (let ((children (children-of-object self parent)))
821 (setf new-child (eval child-initform))
822 (setf new-children
823 (funcall set-child-func
824 (add-to-child-seq parent children new-child)
825 parent))
826 (when (subtypep (controller-type-of self parent) 'hash-table)
827 (setf (ht new-child) parent)
828 (setf (gethash (ht-key new-child) parent) (ht-value new-child))))
829 ;; else see if there is a user-specified function to add a child
830 (when (add-child-func self)
831 (multiple-value-setq (new-children new-child)
832 (funcall (add-child-func self) parent))))
833 (when (added-func self)
834 ;; notify by calling the function specified in IB
835 (let ((last-child (if (typep new-child 'ht-entry)
836 (list (ht-key new-child) (ht-value new-child))
837 new-child)))
838 (when last-child
839 (funcall (added-func self)
840 (owner self)
841 self
842 (root self)
843 parent last-child))))
844 (sort-sequence self new-children)))
845
846(defmethod child-type-of ((self lisp-controller) obj)
847 ;; Get the type of child objects for an instance of some type
848 (let ((obj-type (controller-type-of self obj)))
849 (assoc-aref (type-info self) obj-type :child-type)))
850
851(defmethod col-value ((self lisp-controller) obj col-obj)
852 ;; Get the lisp value for some column for an object
853 ;; return "" if there isn't one so the display doesn't
854 ;; have "nil" for columns without values.
855 (let* ((obj-type (controller-type-of self obj))
856 (override-reader (reader-func self))
857 (reader-func (car (assoc-aref (type-info self) obj-type col-obj))))
858 (if override-reader
859 (funcall override-reader obj (assoc-aref (column-info self) col-obj :col-val))
860 (if reader-func
861 (funcall reader-func obj)
862 ""))))
863
864(defmethod set-col-value ((self lisp-controller) obj col-obj new-value)
865 ;; set the lisp value for some column for an object
866 (let* ((obj-type (controller-type-of self obj))
867 (override-writer (writer-func self))
868 (writer-func (cdr (assoc-aref (type-info self) obj-type col-obj))))
869 (if override-writer
870 (funcall override-writer new-value obj (assoc-aref (column-info self) col-obj :col-val))
871 (if writer-func
872 (funcall writer-func new-value obj)))))
873
874(defmethod remove-child-from ((self lisp-controller) parent child)
875 (let* ((parent-type (controller-type-of self parent))
876 (child-key (assoc-aref (type-info self) parent-type :child-key))
877 (set-child-func (assoc-aref (type-info self) parent-type :child-setf-key))
878 (parent-is-root (eq parent (root self)))
879 (new-children nil))
880 (if (delete-func self)
881 (setf new-children (funcall (delete-func self) parent child))
882 (when (and child-key set-child-func)
883 (let ((children (funcall child-key parent)))
884 (setf new-children
885 (funcall set-child-func
886 (delete-from-child-seq children child)
887 parent)))))
888 (when (and parent-is-root (null new-children))
889 ;; The only time this actually does something is when the
890 ;; objects were a list and we just deleted the last child.
891 (if (listp parent) (setf (root self) nil))
892 (setf (objects self) nil))
893 (when (removed-func self)
894 (funcall (removed-func self) (owner self) self (root self) parent child))
895 (sort-sequence self new-children)))
896
897(defmethod sort-sequence ((self lisp-controller) (seq sequence))
898 ;; sort a sequence of objects
899 (if (plusp (length seq))
900 (let* ((seq-elt-type (controller-type-of self (elt seq 0)))
901 (seq-elt-sort-pred (assoc-aref (type-info self) seq-elt-type :sort-pred))
902 (seq-elt-sort-key (assoc-aref (type-info self) seq-elt-type :sort-key)))
903 (if seq-elt-sort-pred
904 (typecase seq
905 (cons
906 (sort-list-in-place seq seq-elt-sort-pred seq-elt-sort-key))
907 (vector
908 (if seq-elt-sort-key
909 (sort seq seq-elt-sort-pred :key seq-elt-sort-key)
910 (sort seq seq-elt-sort-pred))))
911 seq))
912 seq))
913
914(defmethod sort-sequence ((self lisp-controller) thing)
915 ;; trying to sort something that isn't a sequence
916 ;; just do nothing
917 thing)
918
919;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
920;; Methods invoked by NSTableView objects at runtime.
921;; Needed to be a data source for NSTableView
922
923(objc:defmethod (#/tableView:objectValueForTableColumn:row: :id)
924 ((self lisp-controller)
925 (tab :id)
926 (col :id)
927 (row #>NSInteger))
928 (declare (ignore tab))
929 (let ((ns-format (assoc-aref (column-info self) col :col-format)))
930 (lisp-to-ns-object (col-value self (elt (objects self) row) col) ns-format)))
931
932(objc:defmethod (#/numberOfRowsInTableView: #>NSInteger)
933 ((self lisp-controller) (tab :id))
934 ;; Assumes that objects is some type of sequence
935 ;; Subclass should override this method if that is not true.
936 (declare (ignore tab))
937 (with-slots (root objects count-func owner) self
938 (if root
939 (if count-func
940 (funcall count-func owner self root)
941 (typecase objects
942 (array (if (array-has-fill-pointer-p objects)
943 (fill-pointer objects)
944 (first (array-dimensions objects))))
945 (t
946 (length objects))))
947 0)))
948
949(objc:defmethod (#/tableView:setObjectValue:forTableColumn:row: :void)
950 ((self lisp-controller)
951 (tab :id)
952 (val :id)
953 (col :id)
954 (row #>NSInteger))
955 ;; We let the user edit the table and something was changed
956 ;; try to convert it to the same type as what is already in that
957 ;; position in the objects.
958 (declare (ignore tab))
959 (let* ((row-obj (elt (objects self) row))
960 (old-obj (col-value self row-obj col))
961 (ns-format (assoc-aref (column-info self) col :col-format))
962 (new-val (ns-to-lisp-object old-obj val ns-format)))
963 (if (writer-func self)
964 (funcall (writer-func self)
965 new-val
966 (root self)
967 row
968 (assoc-aref (column-info self) col :col-val))
969 (set-col-value self row-obj col new-val))
970 (when (edited-func self)
971 (let* ((row-obj (object-at-row self row))
972 (edited-obj (if (typep row-obj 'ht-entry)
973 (list (ht-key row-obj) (ht-value row-obj))
974 row-obj)))
975 (funcall (edited-func self)
976 (owner self)
977 self
978 (root self)
979 row
980 (assoc-aref (column-info self) col :col-indx)
981 edited-obj
982 old-obj
983 new-val)))
984 ;; re-sort and reload the table
985 ;; unfortunately we probably have to do this for every change since
986 ;; we don't know what affects the sort order
987 (sort-sequence self (objects self))
988 (#/reloadData (view self))))
989
990(objc:defmethod (#/tableView:shouldEditTableColumn:row: #>BOOL)
991 ((self lisp-controller)
992 (tab :id)
993 (col :id)
994 (row #>NSInteger))
995 (declare (ignore tab))
996 ;; allow editing if there is a function available to setf a new value
997 (if (or (writer-func self)
998 (let ((obj-type (controller-type-of self (elt (objects self) row))))
999 (cdr (assoc-aref (type-info self) obj-type col))))
1000 #$YES
1001 #$NO))
1002
1003(objc:defmethod (#/tableViewSelectionDidChange: :void)
1004 ((self lisp-controller) (notif :id))
1005 (let* ((tab (#/object notif))
1006 (row-indx (#/selectedRow tab))
1007 (col-indx (#/selectedColumn tab)))
1008 ;; enable/disable buttons that remove current selection
1009 (#/willChangeValueForKey: self #@"canRemove")
1010 (if (minusp row-indx)
1011 (setf (can-remove self) #$NO)
1012 (setf (can-remove self) #$YES))
1013 (#/didChangeValueForKey: self #@"canRemove")
1014 ;; enable/disable buttons that want to add a child to
1015 ;; the current selection
1016 (set-can-add-child self row-indx)
1017 ;; User code to do something when a cell is selected
1018 (when (select-func self)
1019 (let* ((row-obj (and (not (eql row-indx -1)) (object-at-row self row-indx)))
1020 (col (assoc-aref (column-info self) col-indx :col-obj))
1021 (selected-obj (cond ((and (minusp col-indx) (minusp row-indx))
1022 nil)
1023 ((minusp col-indx)
1024 row-obj)
1025 ((minusp row-indx)
1026 (assoc-aref (column-info self) col :col-title))
1027 (t
1028 (col-value self row-obj col)))))
1029 (funcall (select-func self)
1030 (owner self)
1031 self
1032 (root self)
1033 row-indx
1034 col-indx
1035 selected-obj)))))
1036
1037;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1038;; Methods invoked by NSOutlineView objects at runtime.
1039;; Needed to be a data source for NSOutlineView.
1040
1041(objc:defmethod (#/outlineView:child:ofItem: :id)
1042 ((self lisp-controller)
1043 (olview :id)
1044 (child #>NSInteger)
1045 (item :id))
1046 (declare (ignore olview))
1047 (with-slots (obj-wrappers objects) self
1048 (cond ((typep item 'lisp-ptr-wrapper)
1049 (let* ((parent (lpw-lisp-ptr item))
1050 (parent-depth (lpw-depth item))
1051 (children (children-of-object self parent))
1052 (child-ptr (elt children child)))
1053 (or (assoc-aref obj-wrappers child-ptr)
1054 (setf (assoc-aref obj-wrappers child-ptr)
1055 (make-ptr-wrapper child-ptr
1056 :depth (1+ parent-depth)
1057 :parent parent)))))
1058 ((eql item (%null-ptr))
1059 (let ((child-ptr (elt objects child)))
1060 (or (assoc-aref obj-wrappers child-ptr)
1061 (setf (assoc-aref obj-wrappers child-ptr)
1062 (make-ptr-wrapper child-ptr :depth 1 :parent nil)))))
1063 (t
1064 (%null-ptr)))))
1065
1066(objc:defmethod (#/outlineView:isItemExpandable: #>BOOL)
1067 ((self lisp-controller)
1068 (olview :id)
1069 (item :id))
1070 (declare (ignore olview))
1071 (cond ((eql item (%null-ptr))
1072 ;; root object
1073 #$YES)
1074 ((typep item 'lisp-ptr-wrapper)
1075 (if (children-of-object self (lpw-lisp-ptr item))
1076 #$YES
1077 #$NO))
1078 (t
1079 #$NO)))
1080
1081(objc:defmethod (#/outlineView:numberOfChildrenOfItem: #>NSInteger)
1082 ((self lisp-controller)
1083 (olview :id)
1084 (item :id))
1085 (declare (ignore olview))
1086 (cond ((typep item 'lisp-ptr-wrapper)
1087 (length (children-of-object self (lpw-lisp-ptr item))))
1088 ((eql item (%null-ptr))
1089 (length (objects self)))
1090 (t
1091 0)))
1092
1093(objc:defmethod (#/outlineView:objectValueForTableColumn:byItem: :id)
1094 ((self lisp-controller)
1095 (olview :id)
1096 (col :id)
1097 (item :id))
1098 (declare (ignore olview))
1099 (let ((ns-format (assoc-aref (column-info self) col :col-format)))
1100 (lisp-to-ns-object (col-value self (lpw-lisp-ptr item) col) ns-format)))
1101
1102(objc:defmethod (#/outlineView:setObjectValue:forTableColumn:byItem: :void)
1103 ((self lisp-controller)
1104 (olview :id)
1105 (val :id)
1106 (col :id)
1107 (item :id))
1108 (let* ((row-obj (lpw-lisp-ptr item))
1109 (old-obj (col-value self row-obj col))
1110 (ns-format (assoc-aref (column-info self) col :col-format))
1111 (new-val (ns-to-lisp-object old-obj val ns-format)))
1112 (if (writer-func self)
1113 (funcall (writer-func self)
1114 new-val
1115 (root self)
1116 row-obj
1117 (assoc-aref (column-info self) col :col-val))
1118 (set-col-value self row-obj col new-val))
1119 (when (edited-func self)
1120 (let* ((row (#/rowForItem: olview item))
1121 (edited-obj (if (typep row-obj 'ht-entry)
1122 (list (ht-key row-obj) (ht-value row-obj))
1123 row-obj)))
1124 (funcall (edited-func self)
1125 (owner self)
1126 self
1127 (root self)
1128 row
1129 (assoc-aref (column-info self) col :col-val)
1130 edited-obj
1131 old-obj
1132 new-val)))))
1133
1134(objc:defmethod (#/outlineView:shouldEditTableColumn:item: #>BOOL)
1135 ((self lisp-controller)
1136 (olview :id)
1137 (col :id)
1138 (item :id))
1139 (declare (ignore olview))
1140 ;; allow editing if there is a function available to setf a new value
1141 (if (or (writer-func self)
1142 (let ((obj-type (controller-type-of self (lpw-lisp-ptr item))))
1143 (cdr (assoc-aref (type-info self) obj-type col))))
1144 #$YES
1145 #$NO))
1146
1147;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1148;; Methods for inserting and removing rows. User interface can trigger these
1149;; (for example) by setting action methods on buttons
1150
1151(objc:defmethod (#/insert: :void)
1152 ((self lisp-controller) (button :id))
1153 (declare (ignore button))
1154 ;; insert a new object into the root object
1155 (unless (root self)
1156 ;; need to create a root object
1157 ;; may still be null if root type is 'list
1158 (setf (root self)
1159 (new-object-of-type self (root-type self))))
1160 (let ((new-children (add-child-to self (root self))))
1161 (when (null (root self))
1162 ;; special hack for root objects that are lists and may be null
1163 (setf (root self) new-children)
1164 (setf (objects self) new-children)))
1165 (#/reloadData (view self)))
1166
1167(objc:defmethod (#/addChild: :void)
1168 ((self lisp-controller) (button :id))
1169 (declare (ignore button))
1170 ;; add a new child to the currently selected item
1171 (let* ((row-num (#/selectedRow (view self)))
1172 (parent (object-at-row self row-num)))
1173 (add-child-to self parent))
1174 (#/reloadData (view self)))
1175
1176(objc:defmethod (#/remove: :void)
1177 ((self lisp-controller) (button :id))
1178 (declare (ignore button))
1179 (let ((row-num (#/selectedRow (view self))))
1180 (multiple-value-bind (child parent) (object-at-row self row-num)
1181 (when parent
1182 (remove-child-from self parent child)
1183 (#/reloadData (view self))))))
1184
1185(provide :lisp-controller)
1186
Note: See TracBrowser for help on using the repository browser.