Changeset 14591
- Timestamp:
- Jan 23, 2011, 8:25:29 AM (14 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/cocoa-ide-contrib/krueger/InterfaceProjects/Utilities/ns-object-utils.lisp
r14585 r14591 35 35 (:export 36 36 archive-slots 37 bound-slot-modified 37 38 class-name-string 38 39 clear-instance-hash … … 246 247 ((ccl::subclassp lisp-class (find-class 'symbol)) 247 248 (string-to-interned-symbol lisp-str)) 249 ((ccl::subclassp lisp-class (find-class 'number)) 250 (let ((num (read-from-string lisp-str :nil 0))) 251 (if (numberp num) 252 num 253 0))) 248 254 (t 249 255 lisp-str)))) … … 697 703 (ccl::%get-selector (ccl::load-objc-selector (concatenate 'string "set" (string-capitalize str :end 1))))) 698 704 699 (objc:defmethod (#/valueForKey: :id) 700 ((self lisp-ptr-wrapper) (path :id)) 701 ;; Treat path as a lisp path as long as it works. 702 ;; If it is not valid and the next target is an Objective-C object 703 ;; then treat the path as a normal Objective-C Key and return the results 704 ;; of calling #/valueForKey: on the target using path as the key. 705 (let* ((lisp-str (ns-to-lisp-string path)) 706 (lisp-path (objc-to-lisp-keypathname lisp-str)) 707 (ptr-obj (lpw-lisp-ptr self)) 708 ;; to set next-obj we try 3 ways: 709 ;; 1. If the path is a valid lisp function name use it to access the slot 710 ;; 2. If the object is an Objective-C object try calling its #/valueForKey method 711 ;; 3. Look for any KVO slots defined for the object with path specified as the KVO 712 ;; accessor and use the lisp function value-for-kvo-key to access that slot 713 (next-obj (cond ((and (typep lisp-path 'function-name) 714 (fboundp lisp-path)) 715 (funcall lisp-path ptr-obj)) 716 ((and (typep ptr-obj 'objc:objc-object) 717 (#/respondsToSelector: ptr-obj (reader-selector lisp-str))) 718 (#/valueForKey: ptr-obj path)) 719 (t 720 (ccl::value-for-kvo-key ptr-obj lisp-str))))) 721 ;; First track that the path is being observed by somebody 722 (note-kvc-observed self (lpw-lisp-ptr self) lisp-path) 723 (note-kvc-observed self (lpw-lisp-ptr self) lisp-str) 724 ;; (ns-log (format nil "(~s ~s) returned ~s" lisp-path (lpw-lisp-ptr self) next-obj)) 725 (cond ((eql next-obj (%null-ptr)) 726 next-obj) 727 ((null next-obj) 728 (%null-ptr)) 729 ((and (typep next-obj 'ns:ns-object) 730 (not (typep next-obj 'ccl::kvo-object))) 731 ;; any kvo-objects will be encapsulated in a lisp-ptr-wrapper 732 ;; so that subsequent accesses through this method will try to 733 ;; use ccl::value-for-kvo-key 734 next-obj) 735 ((typep next-obj 'objc-displayable) 736 (lisp-to-ns-object next-obj)) 737 (t 738 (wrapper-for (lpw-controller self) next-obj :parent (lpw-lisp-ptr self)))))) 739 740 (objc:defmethod (#/setValue:forKey: :void) 741 ((self lisp-ptr-wrapper) (new-value :id) (path :id)) 742 (let* ((lisp-str (ns-to-lisp-string path)) 743 (lisp-path (objc-to-lisp-keypathname lisp-str)) 744 (ptr-obj (lpw-lisp-ptr self)) 745 (prev-obj (cond ((and (typep lisp-path 'function-name) 746 (fboundp lisp-path)) 747 (funcall lisp-path ptr-obj)) 748 ((and (typep ptr-obj 'objc:objc-object) 749 (#/respondsToSelector: self (reader-selector lisp-str))) 750 (#/valueForKey: ptr-obj path)) 751 (t 752 (ccl::value-for-kvo-key ptr-obj lisp-str)))) 753 (prev-class (class-of prev-obj)) 754 (new-lisp-obj (ns-to-lisp-object new-value :lisp-class prev-class)) 755 (setf-func (fboundp (list 'setf lisp-path))) 756 ;;`(setf (,lisp-path ,(lpw-lisp-ptr self)) ,new-lisp-obj)) 757 (ctrl (lpw-controller self))) 758 ;; (ns-log (format nil "Prev Class: ~s" prev-class)) 759 (cond (setf-func 760 (funcall setf-func new-lisp-obj ptr-obj)) 761 ((and (typep ptr-obj 'objc:objc-object) 762 (typep new-lisp-obj 'objc:objc-object) 763 (#/respondsToSelector: self (writer-selector lisp-str))) 764 (#/setValue:forKey: ptr-obj new-lisp-obj path)) 765 (t 766 (multiple-value-bind (value found-slot) 767 (setf (ccl::value-for-kvo-key ptr-obj lisp-str) new-lisp-obj) 768 (declare (ignore value)) 769 (unless found-slot 770 ;; If the setf failed, log the original condition 771 (error "No way to setValue: ~s forKey: ~s for lisp-ptr ~s" 772 new-lisp-obj 773 lisp-str 774 ptr-obj))))) 775 (when ctrl 776 (lc::modified-bound-value ctrl 777 (lpw-lisp-ptr self) 778 lisp-path 779 prev-obj 780 new-lisp-obj)) 781 new-lisp-obj)) 705 (defmethod bound-slot-modified ((self standard-object) slot-name) 706 (declare (ignore slot-name)) 707 ;; do nothing by default 708 ) 709 710 (defmethod real-observer (nskvo) 711 ;; nskvo will be an instance of NSKeyValueObservance which isn't public as far as I can tell. 712 ;; All we want is a pointer to the original observer extracted from that object, but without 713 ;; a public API that is a little tricky. But we are trickier. We use the #/description of that 714 ;; object which provides a substring of the form "Observer: 0x<whatever>" which points to 715 ;; possibly another non-public object of type NSSelectionBinder. We extract the address and 716 ;; create a pointer to the NSSelectionbinder. We now use its description to extract its 717 ;; "object" slot address, which hopefully points to the original view object that we bound 718 ;; to our lisp object. We use that to create a macptr and return it. 719 (let* ((desc (ns-to-lisp-string (#/description nskvo))) 720 (obs-start (search "Observer: " desc)) 721 (hex-start (and obs-start (position #\x desc :start (+ obs-start 9)))) 722 (hex-end (and hex-start (position #\, desc :start hex-start))) 723 (hex-val (and hex-end (read-from-string (concatenate 'string "#" (subseq desc hex-start hex-end)) nil 0))) 724 (nssb-ptr (and hex-val (ccl::%int-to-ptr hex-val))) 725 (nssb-desc (and nssb-ptr (ns-to-lisp-string (#/description nssb-ptr)))) 726 (obj-start (search "object:" nssb-desc)) 727 (obj-class-end (and obj-start (position #\: nssb-desc :start (+ obj-start 8)))) 728 (obj-hex-start (and obj-class-end (position #\x nssb-desc :start obj-class-end))) 729 (obj-hex-end (and obj-hex-start (position #\> nssb-desc :start obj-hex-start))) 730 (obj-hex-val (and obj-hex-end (read-from-string (concatenate 'string "#" (subseq nssb-desc obj-hex-start obj-hex-end)) nil 0)))) 731 (when obj-hex-val 732 (ccl::%int-to-ptr obj-hex-val)))) 733 734 (let ((format-assoc (make-instance 'assoc-array :rank 2 :tests (list #'eql #'equal)))) 735 ;; used to hold needed Objective-C format information for slots that are observed by 736 ;; Objective-C objects that have associated formatter objects from which we can take hints. 737 738 (objc:defmethod (#/addObserver:forKeyPath:options:context: :void) 739 ((self lisp-ptr-wrapper) (obs :id) (key-path :id) (options #>NSUInteger) (context :address)) 740 (let ((observer (real-observer obs))) 741 (ns-log (format nil "~s observed by ~s" (lpw-lisp-ptr self) observer)) 742 (when (subtypep (type-of observer) 'ns:ns-control) 743 (let* ((cell (#/cell observer)) 744 (formatter (#/formatter cell)) 745 (ns-format nil)) 746 (cond ((or (typep cell 'ns:ns-date-picker-cell) 747 (typep formatter 'ns:ns-date-formatter)) 748 (setf ns-format :date)) 749 ((typep formatter 'ns:ns-number-formatter) 750 (cond ((#/generatesDecimalNumbers formatter) 751 (let ((dec-digits (#/maximumFractionDigits formatter))) 752 (setf ns-format (list :decimal dec-digits)))) 753 (t 754 (setf ns-format :number))))) 755 (when ns-format 756 (ns-log (format nil "Observer data format: ~s" ns-format)) 757 (setf (assoc-aref format-assoc self (ns-to-lisp-string key-path)) ns-format))))) 758 (call-next-method obs key-path options context)) 759 760 (objc:defmethod (#/valueForKey: :id) 761 ((self lisp-ptr-wrapper) (path :id)) 762 ;; Treat path as a lisp path as long as it works. 763 ;; If it is not valid and the next target is an Objective-C object 764 ;; then treat the path as a normal Objective-C Key and return the results 765 ;; of calling #/valueForKey: on the target using path as the key. 766 (let* ((lisp-str (ns-to-lisp-string path)) 767 (lisp-path (objc-to-lisp-keypathname lisp-str)) 768 (ptr-obj (lpw-lisp-ptr self)) 769 ;; to set next-obj we try 3 ways: 770 ;; 1. If the path is a valid lisp function name use it to access the slot 771 ;; 2. If the object is an Objective-C object try calling its #/valueForKey method 772 ;; 3. Look for any KVO slots defined for the object with path specified as the KVO 773 ;; accessor and use the lisp function value-for-kvo-key to access that slot 774 (next-obj (cond ((and (typep lisp-path 'function-name) 775 (fboundp lisp-path)) 776 (funcall lisp-path ptr-obj)) 777 ((and (typep ptr-obj 'objc:objc-object) 778 (#/respondsToSelector: ptr-obj (reader-selector lisp-str))) 779 (#/valueForKey: ptr-obj path)) 780 (t 781 (ccl::value-for-kvo-key ptr-obj lisp-str))))) 782 ;; First track that the path is being observed by somebody 783 (note-kvc-observed self (lpw-lisp-ptr self) lisp-path) 784 (note-kvc-observed self (lpw-lisp-ptr self) lisp-str) 785 ;; (ns-log (format nil "(~s ~s) returned ~s" lisp-path (lpw-lisp-ptr self) next-obj)) 786 (cond ((eql next-obj (%null-ptr)) 787 next-obj) 788 ((null next-obj) 789 (%null-ptr)) 790 ((and (typep next-obj 'ns:ns-object) 791 (not (typep next-obj 'ccl::kvo-object))) 792 ;; any kvo-objects will be encapsulated in a lisp-ptr-wrapper 793 ;; so that subsequent accesses through this method will try to 794 ;; use ccl::value-for-kvo-key 795 next-obj) 796 ((typep next-obj 'objc-displayable) 797 (lisp-to-ns-object next-obj (assoc-aref format-assoc self lisp-str))) 798 (t 799 (wrapper-for (lpw-controller self) next-obj :parent (lpw-lisp-ptr self)))))) 800 801 (objc:defmethod (#/setValue:forKey: :void) 802 ((self lisp-ptr-wrapper) (new-value :id) (path :id)) 803 (let* ((lisp-str (ns-to-lisp-string path)) 804 (lisp-path (objc-to-lisp-keypathname lisp-str)) 805 (ptr-obj (lpw-lisp-ptr self)) 806 (prev-obj (cond ((and (typep lisp-path 'function-name) 807 (fboundp lisp-path)) 808 (funcall lisp-path ptr-obj)) 809 ((and (typep ptr-obj 'objc:objc-object) 810 (#/respondsToSelector: self (reader-selector lisp-str))) 811 (#/valueForKey: ptr-obj path)) 812 (t 813 (ccl::value-for-kvo-key ptr-obj lisp-str)))) 814 (prev-class (class-of prev-obj)) 815 (new-lisp-obj (ns-to-lisp-object new-value :lisp-class prev-class)) 816 (setf-func (fboundp (list 'setf lisp-path))) 817 ;;`(setf (,lisp-path ,(lpw-lisp-ptr self)) ,new-lisp-obj)) 818 (ctrl (lpw-controller self))) 819 ;; (ns-log (format nil "Prev Class: ~s" prev-class)) 820 (cond (setf-func 821 (funcall setf-func new-lisp-obj ptr-obj)) 822 ((and (typep ptr-obj 'objc:objc-object) 823 (typep new-lisp-obj 'objc:objc-object) 824 (#/respondsToSelector: self (writer-selector lisp-str))) 825 (#/setValue:forKey: ptr-obj new-lisp-obj path)) 826 (t 827 (multiple-value-bind (value found-slot) 828 (setf (ccl::value-for-kvo-key ptr-obj lisp-str) new-lisp-obj) 829 (declare (ignore value)) 830 (if found-slot 831 (bound-slot-modified ptr-obj (ccl::slot-definition-name found-slot)) 832 ;; If the setf failed, log the original condition 833 (error "No way to setValue: ~s forKey: ~s for lisp-ptr ~s" 834 new-lisp-obj 835 lisp-str 836 ptr-obj))))) 837 (when ctrl 838 (lc::modified-bound-value ctrl 839 (lpw-lisp-ptr self) 840 lisp-path 841 prev-obj 842 new-lisp-obj)) 843 new-lisp-obj)) 844 845 ) 782 846 783 847 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Note:
See TracChangeset
for help on using the changeset viewer.
