Changeset 707
- Timestamp:
- Mar 22, 2004, 9:41:04 AM (21 years ago)
- File:
-
- 1 edited
-
trunk/ccl/examples/cocoa-editor.lisp (modified) (27 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/ccl/examples/cocoa-editor.lisp
r678 r707 177 177 ;;; Ask Hemlock to count the characters in the buffer. 178 178 (defun hemlock-buffer-length (buffer) 179 (hemlock::count-characters (hemlock::buffer-region buffer))) 179 (hi::with-buffer-gap-info (buffer) 180 (hemlock::count-characters (hemlock::buffer-region buffer)))) 180 181 181 182 ;;; Find the line containing (or immediately preceding) index, which is … … 183 184 ;;; in that line or the trailing #\newline, as appropriate. 184 185 (defun hemlock-char-at-index (cache index) 185 (multiple-value-bind (line idx) (update-line-cache-for-index cache index) 186 (let* ((len (hemlock::line-length line))) 187 (if (< idx len) 188 (hemlock::line-character line idx) 189 #\newline)))) 186 (hi::with-buffer-gap-info ((buffer-cache-buffer cache)) 187 (multiple-value-bind (line idx) (update-line-cache-for-index cache index) 188 (let* ((len (hemlock::line-length line))) 189 (if (< idx len) 190 (hemlock::line-character line idx) 191 #\newline))))) 190 192 191 193 ;;; Given an absolute position, move the specified mark to the appropriate 192 194 ;;; offset on the appropriate line. 193 195 (defun move-hemlock-mark-to-absolute-position (mark cache abspos) 194 (multiple-value-bind (line idx) (update-line-cache-for-index cache abspos) 195 (hemlock::move-to-position mark idx line))) 196 (hi::with-buffer-gap-info ((buffer-cache-buffer cache)) 197 (multiple-value-bind (line idx) (update-line-cache-for-index cache abspos) 198 (hemlock::move-to-position mark idx line)))) 196 199 197 200 ;;; Return the absolute position of the mark in the containing buffer. … … 199 202 ;;; number of preceding lines. 200 203 (defun mark-absolute-position (mark) 201 (let* ((pos (hemlock::mark-charpos mark))) 202 (do* ((line (hemlock::line-previous (hemlock::mark-line mark)) 203 (hemlock::line-previous line))) 204 ((null line) pos) 205 (incf pos (1+ (hemlock::line-length line)))))) 204 (hi::with-buffer-gap-info ((hi::line-%buffer (hi::mark-line mark))) 205 (let* ((pos (hi::mark-charpos mark))) 206 (do* ((line (hi::line-previous (hi::mark-line mark)) 207 (hi::line-previous line))) 208 ((null line) pos) 209 (incf pos (1+ (hi::line-length line))))))) 206 210 207 211 ;;; Return the length of the abstract string, i.e., the number of … … 212 216 (or (buffer-cache-buflen cache) 213 217 (setf (buffer-cache-buflen cache) 214 (hemlock-buffer-length (buffer-cache-buffer cache)))))) 218 (let* ((buffer (buffer-cache-buffer cache))) 219 (hi::with-buffer-gap-info (buffer) 220 (hemlock-buffer-length buffer))))))) 215 221 216 222 … … 232 238 (external-format (if buffer (hi::buffer-external-format buffer ))) 233 239 (raw-length (if buffer (hemlock-buffer-length buffer) 0))) 240 234 241 (if (eql 0 raw-length) 235 242 (make-objc-instance 'ns:ns-mutable-data :with-length 0) … … 282 289 283 290 284 ;;; Lisp-text-storage objects285 (defclass lisp-text-storage (ns:ns-text-storage)291 ;;; hemlock-text-storage objects 292 (defclass hemlock-text-storage (ns:ns-text-storage) 286 293 ((string :foreign-type :id)) 287 294 (:metaclass ns:+ns-object)) … … 289 296 ;;; Access the string. It'd be nice if this was a generic function; 290 297 ;;; we could have just made a reader method in the class definition. 291 (define-objc-method ((:id string) lisp-text-storage)298 (define-objc-method ((:id string) hemlock-text-storage) 292 299 (slot-value self 'string)) 293 300 294 (define-objc-method ((:id :init-with-string s) lisp-text-storage)301 (define-objc-method ((:id :init-with-string s) hemlock-text-storage) 295 302 (let* ((newself (send-super 'init))) 296 303 (setf (slot-value newself 'string) s) … … 298 305 299 306 ;;; This is the only thing that's actually called to create a 300 ;;; lisp-text-storage object. (It also creates the underlying307 ;;; hemlock-text-storage object. (It also creates the underlying 301 308 ;;; hemlock-buffer-string.) 302 309 (defun make-textstorage-for-hemlock-buffer (buffer) 303 (make-objc-instance ' lisp-text-storage310 (make-objc-instance 'hemlock-text-storage 304 311 :with-string 305 312 (make-instance … … 314 321 (define-objc-method ((:id :attributes-at-index (:unsigned index) 315 322 :effective-range ((* :<NSR>ange) rangeptr)) 316 lisp-text-storage)323 hemlock-text-storage) 317 324 (declare (ignorable index)) 318 325 (let* ((buffer-cache (hemlock-buffer-string-cache (slot-value self 'string))) … … 328 335 (define-objc-method ((:void :replace-characters-in-range (:<NSR>ange r) 329 336 :with-string string) 330 lisp-text-storage)337 hemlock-text-storage) 331 338 (#_NSLog #@"replace-characters-in-range (%d %d) with-string %@" 332 339 :unsigned (pref r :<NSR>ange.location) … … 338 345 (define-objc-method ((:void :set-attributes attributes 339 346 :range (:<NSR>ange r)) 340 lisp-text-storage)347 hemlock-text-storage) 341 348 (#_NSLog #@"set-attributes %@ range (%d %d)" 342 349 :id attributes … … 344 351 :unsigned (pref r :<NSR>ange.length))) 345 352 353 (defun for-each-textview-using-storage (textstorage f) 354 (let* ((layouts (send textstorage 'layout-managers))) 355 (unless (%null-ptr-p layouts) 356 (dotimes (i (send layouts 'count)) 357 (let* ((layout (send layouts :object-at-index i)) 358 (containers (send layout 'text-containers))) 359 (unless (%null-ptr-p containers) 360 (dotimes (j (send containers 'count)) 361 (let* ((container (send containers :object-at-index j)) 362 (tv (send container 'text-view))) 363 (funcall f tv))))))))) 346 364 347 365 ;;; Again, it's helpful to see the buffer name when debugging. 348 366 (define-objc-method ((:id description) 349 lisp-text-storage)367 hemlock-text-storage) 350 368 (send (@class ns-string) :string-with-format #@"%s : string %@" 351 369 (:address (#_object_getClassName self) :id (slot-value self 'string)))) 370 371 ;;; This needs to happen on the main thread. 372 (define-objc-method ((:void ensure-selection-visible) 373 hemlock-text-storage) 374 (for-each-textview-using-storage 375 self 376 #'(lambda (tv) 377 (send tv :scroll-range-to-visible (send tv 'selected-range))))) 378 379 ;;; This needs to run on the main thread. 380 (define-objc-method ((void update-hemlock-selection) 381 hemlock-text-storage) 382 (let* ((string (send self 'string)) 383 (buffer (buffer-cache-buffer (hemlock-buffer-string-cache string))) 384 (point (hi::buffer-point buffer)) 385 (pos (mark-absolute-position point))) 386 (for-each-textview-using-storage 387 self 388 #'(lambda (tv) 389 (slet ((selection (ns-make-range pos 0))) 390 (send tv :set-selected-range selection)))))) 391 392 352 393 353 394 (defun close-hemlock-textstorage (ts) … … 418 459 (let* ((buffer (text-view-buffer self))) 419 460 (when buffer 420 (let* ((info (hemlock-frame-command-info (send self 'window)))) 421 (when info 422 (let* ((key-event (nsevent-to-key-event event))) 423 (when event 424 (unless (eq buffer hi::*current-buffer*) 425 (setf (hi::current-buffer) buffer)) 426 (let* ((pane (text-view-pane self))) 427 (unless (eql pane (hi::current-window)) 428 (setf (hi::current-window) pane))) 429 #+debug 430 (format t "~& key-event = ~s" key-event) 431 (let* ((w (send self 'window)) 432 (hi::*echo-area-buffer* (hemlock-frame-echo-area-buffer w)) 433 (hi::*echo-area-stream* 434 (hemlock-frame-echo-area-stream w)) 435 (hi::*echo-area-window* (slot-value w 'echo-area-view )) 436 (hi::*echo-area-region* 437 (hi::buffer-region hi::*echo-area-buffer*))) 438 (hi::interpret-key-event key-event info))))))))) 461 (let* ((q (hemlock-frame-event-queue (send self 'window)))) 462 (hi::enqueue-key-event q (nsevent-to-key-event event))))) 463 ;; Probably not the right place for this, but needs to happen 464 ;; -somewhere-, and needs to happen in the event thread. 465 (send self :scroll-range-to-visible (send self 'selected-range)) 466 ) 467 468 (defun enqueue-buffer-operation (buffer thunk) 469 (dolist (w (hi::buffer-windows buffer)) 470 (let* ((q (hemlock-frame-event-queue (send w 'window))) 471 (op (hi::make-buffer-operation :thunk thunk))) 472 (hi::enqueue-key-event q op)))) 473 439 474 440 ;;; Process a key-down NSEvent in a lisptext view by translating it475 ;;; Process a key-down NSEvent in a Hemlock text view by translating it 441 476 ;;; into a Hemlock key event and passing it into the Hemlock command 442 477 ;;; interpreter. The underlying buffer becomes Hemlock's current buffer … … 709 744 (send hemlock-frame :make-first-responder view))) 710 745 746 (defmethod text-view-buffer ((self echo-area-view)) 747 (buffer-cache-buffer (hemlock-buffer-string-cache (send (send self 'text-storage) 'string)))) 748 711 749 ;;; The "document" for an echo-area isn't a real NSDocument. 712 750 (defclass echo-area-document (ns:ns-object) … … 732 770 (incf *hemlock-frame-count*))) 733 771 :modes '("Echo Area"))) 734 (stream (hi::make-hemlock-output-stream735 (hi::region-end (hi::buffer-region buffer)) :full))736 772 (textstorage (make-textstorage-for-hemlock-buffer buffer)) 737 773 (doc (make-objc-instance 'echo-area-document)) … … 756 792 (send container :set-height-tracks-text-view nil) 757 793 (setf (hemlock-frame-echo-area-buffer hemlock-frame) buffer 758 (hemlock-frame-echo-area-stream hemlock-frame) stream759 794 (slot-value doc 'textstorage) textstorage 760 795 (hi::buffer-document buffer) doc) … … 769 804 echo-area)))) 770 805 771 772 (defmethod hemlock-frame-command-info ((w ns:ns-window))773 nil)774 775 776 806 (defclass hemlock-frame (ns:ns-window) 777 807 ((echo-area-view :foreign-type :id) 778 (command-info :initform (hi::make-command-interpreter-info) 779 :accessor hemlock-frame-command-info) 808 (event-queue :initform (ccl::init-dll-header (hi::make-frame-event-queue)) 809 :reader hemlock-frame-event-queue) 810 (command-thread :initform nil) 780 811 (echo-area-buffer :initform nil :accessor hemlock-frame-echo-area-buffer) 781 812 (echo-area-stream :initform nil :accessor hemlock-frame-echo-area-stream)) 782 813 (:metaclass ns:+ns-object)) 783 814 784 785 (defmethod shared-initialize :after ((w hemlock-frame) 786 slot-names 787 &key &allow-other-keys) 788 (declare (ignore slot-names)) 789 (let ((info (hemlock-frame-command-info w))) 790 (when info 791 (setf (hi::command-interpreter-info-frame info) w)))) 792 793 794 795 796 797 815 (defun hemlock-thread-function (q buffer pane echo-buffer echo-window) 816 (let* ((hi::*real-editor-input* q) 817 (hi::*editor-input* q) 818 (hi::*current-buffer* hi::*current-buffer*) 819 (hi::*current-window* pane) 820 (hi::*echo-area-window* echo-window) 821 (hi::*echo-area-buffer* echo-buffer) 822 (region (hi::buffer-region echo-buffer)) 823 (hi::*echo-area-region* region) 824 (hi::*echo-area-stream* (hi::make-hemlock-output-stream 825 (hi::region-end region) :full)) 826 (hi::*cache-modification-tick* -1) 827 (hi::now-tick 0) 828 (hi::*disembodied-buffer-counter* 0) 829 (hi::*in-a-recursive-edit* nil) 830 (hi::*last-key-event-typed* nil) 831 (hi::*input-transcript* nil) 832 (hi::*line-cache-length* 200) 833 (hi::*open-line* nil) 834 (hi::*open-chars* (make-string hi::*line-cache-length* )) 835 (hi::*left-open-pos* 0) 836 (hi::*right-open-pos* 0) 837 (hemlock::*target-column* 0) 838 (hemlock::*last-comment-start* 0) 839 (hemlock::*last-search-string* ()) 840 (hemlock::*last-search-pattern* 841 (hemlock::new-search-pattern :string-insensitive :forward "Foo")) 842 ) 843 (setf (hi::current-buffer) buffer) 844 (hi::%command-loop))) 845 846 847 (define-objc-method ((:void close) hemlock-frame) 848 (let* ((proc (slot-value self 'command-thread))) 849 (when proc 850 (setf (slot-value self 'command-thread) nil) 851 (process-kill proc))) 852 (send-super 'close)) 853 798 854 (defun new-hemlock-document-window () 799 855 (let* ((w (new-cocoa-window :class (find-class 'hemlock-frame) … … 915 971 ;;; This function must run in the main event thread. 916 972 (defun %hemlock-frame-for-textstorage (ts ncols nrows container-tracks-text-view-width) 917 (let* ((pane (textpane-for-textstorage ts ncols nrows container-tracks-text-view-width))) 918 (send pane 'window))) 973 (let* ((pane (textpane-for-textstorage ts ncols nrows container-tracks-text-view-width)) 974 (frame (send pane 'window)) 975 (buffer (text-view-buffer (text-pane-text-view pane)))) 976 (setf (slot-value frame 'command-thread) 977 (process-run-function (format nil "Hemlock window thread") 978 #'(lambda () 979 (hemlock-thread-function 980 (hemlock-frame-event-queue frame) 981 buffer 982 pane 983 (hemlock-frame-echo-area-buffer frame) 984 (slot-value frame 'echo-area-view))))) 985 frame)) 986 987 919 988 920 989 … … 925 994 926 995 927 (defun for-each-textview-using-storage (textstorage f)928 (let* ((layouts (send textstorage 'layout-managers)))929 (unless (%null-ptr-p layouts)930 (dotimes (i (send layouts 'count))931 (let* ((layout (send layouts :object-at-index i))932 (containers (send layout 'text-containers)))933 (unless (%null-ptr-p containers)934 (dotimes (j (send containers 'count))935 (let* ((container (send containers :object-at-index j))936 (tv (send container 'text-view)))937 (funcall f tv)))))))))938 996 939 997 940 998 941 999 (defun hi::document-begin-editing (document) 942 (send (slot-value document 'textstorage) 'begin-editing)) 1000 (send (slot-value document 'textstorage) 1001 :perform-selector-on-main-thread 1002 (@selector "beginEditing") 1003 :with-object (%null-ptr) 1004 :wait-until-done t)) 1005 1006 943 1007 944 1008 (defun hi::document-end-editing (document) 1009 (send (slot-value document 'textstorage) 1010 :perform-selector-on-main-thread 1011 (@selector "endEditing") 1012 :with-object (%null-ptr) 1013 :wait-until-done t)) 1014 1015 (defun hi::document-set-point-position (document) 945 1016 (let* ((textstorage (slot-value document 'textstorage))) 946 (send textstorage 'end-editing) 947 (for-each-textview-using-storage 948 textstorage 949 #'(lambda (tv) 950 (send tv :scroll-range-to-visible (send tv 'selected-range)))))) 951 952 (defun hi::document-set-point-position (document) 953 (let* ((textstorage (slot-value document 'textstorage)) 954 (string (send textstorage 'string)) 955 (buffer (buffer-cache-buffer (hemlock-buffer-string-cache string))) 956 (point (hi::buffer-point buffer)) 957 (pos (mark-absolute-position point))) 958 (for-each-textview-using-storage 959 textstorage 960 #'(lambda (tv) 961 (slet ((selection (ns-make-range pos 0))) 962 (send tv :set-selected-range selection)))))) 1017 (send textstorage 1018 :perform-selector-on-main-thread 1019 (@selector "updateHemlockSelection") 1020 :with-object (%null-ptr) 1021 :wait-until-done t))) 963 1022 964 1023 … … 1060 1119 1061 1120 1062 (defclass lisp-editor-window-controller (ns:ns-window-controller)1121 (defclass hemlock-editor-window-controller (ns:ns-window-controller) 1063 1122 () 1064 1123 (:metaclass ns:+ns-object)) 1065 1124 1066 1125 1067 ;;; The LispEditorWindowController is the textview's "delegate": it 1068 ;;; gets consulted before certain actions are performed, and can 1069 ;;; perform actions on behalf of the textview. 1070 1071 1072 1073 ;;; The LispEditorDocument class. 1074 1075 1076 (defclass lisp-editor-document (ns:ns-document) 1126 1127 1128 ;;; The HemlockEditorDocument class. 1129 1130 1131 (defclass hemlock-editor-document (ns:ns-document) 1077 1132 ((textstorage :foreign-type :id)) 1078 1133 (:metaclass ns:+ns-object)) 1079 1134 1080 (define-objc-method ((:id init) lisp-editor-document)1135 (define-objc-method ((:id init) hemlock-editor-document) 1081 1136 (let* ((doc (send-super 'init))) 1082 1137 (unless (%null-ptr-p doc) 1083 1138 (let* ((buffer (make-hemlock-buffer 1084 1139 (lisp-string-from-nsstring (send doc 'display-name)) 1085 :modes '("Lisp" ))))1140 :modes '("Lisp" "Editor")))) 1086 1141 (setf (slot-value doc 'textstorage) 1087 1142 (make-textstorage-for-hemlock-buffer buffer) … … 1092 1147 (define-objc-method ((:id :read-from-file filename 1093 1148 :of-type type) 1094 lisp-editor-document)1149 hemlock-editor-document) 1095 1150 (declare (ignorable type)) 1096 1151 (let* ((pathname (lisp-string-from-nsstring filename)) … … 1130 1185 (when cache (buffer-cache-buffer cache)))))) 1131 1186 1132 (defmethod hi::document-panes ((document lisp-editor-document))1187 (defmethod hi::document-panes ((document hemlock-editor-document)) 1133 1188 (let* ((ts (slot-value document 'textstorage)) 1134 1189 (panes ())) … … 1142 1197 1143 1198 (define-objc-method ((:id :data-representation-of-type type) 1144 lisp-editor-document)1199 hemlock-editor-document) 1145 1200 (declare (ignorable type)) 1146 1201 (let* ((buffer (hemlock-document-buffer self))) … … 1155 1210 ;;; name and pathname in synch with the document. 1156 1211 (define-objc-method ((:void :set-file-name full-path) 1157 lisp-editor-document)1212 hemlock-editor-document) 1158 1213 (send-super :set-file-name full-path) 1159 1214 (let* ((buffer (hemlock-document-buffer self))) … … 1163 1218 (setf (hi::buffer-pathname buffer) new-pathname))))) 1164 1219 1165 (define-objc-method ((:void make-window-controllers) lisp-editor-document)1220 (define-objc-method ((:void make-window-controllers) hemlock-editor-document) 1166 1221 (let* ((controller (make-objc-instance 1167 ' lisp-editor-window-controller1222 'hemlock-editor-window-controller 1168 1223 :with-window (%hemlock-frame-for-textstorage 1169 1224 (slot-value self 'textstorage) … … 1174 1229 (send controller 'release))) 1175 1230 1176 #| 1177 (define-objc-method ((:void :window-controller-did-load-nib acontroller) 1178 lisp-editor-document) 1179 (send-super :window-controller-did-load-nib acontroller) 1180 ;; Apple/NeXT thinks that adding extra whitespace around cut & pasted 1181 ;; text is "smart". Really, really smart insertion and deletion 1182 ;; would alphabetize the selection for you (byChars: or byWords:); 1183 ;; sadly, if you want that behavior you'll have to do it yourself. 1184 ;; Likewise with the extra spaces. 1185 (with-slots (text-view echoarea packagename filedata) self 1186 (send text-view :set-alignment #$NSNaturalTextAlignment) 1187 (send text-view :set-smart-insert-delete-enabled nil) 1188 (send text-view :set-rich-text nil) 1189 (send text-view :set-uses-font-panel t) 1190 (send text-view :set-uses-ruler nil) 1191 (with-lock-grabbed (*open-editor-documents-lock*) 1192 (push (make-cocoa-editor-info 1193 :document (%setf-macptr (%null-ptr) self) 1194 :controller (%setf-macptr (%null-ptr) acontroller) 1195 :listener nil) 1196 *open-editor-documents*)) 1197 (setf (slot-value acontroller 'textview) text-view 1198 (slot-value acontroller 'echoarea) echoarea 1199 (slot-value acontroller 'packagename) packagename) 1200 (send text-view :set-delegate acontroller) 1201 (let* ((font (default-font))) 1202 (multiple-value-bind (height width) 1203 (size-of-char-in-font font) 1204 (size-textview-containers text-view height width 24 80)) 1205 (send text-view 1206 :set-typing-attributes 1207 (create-text-attributes 1208 :font font 1209 :color (send (@class ns-color) 'black-color))) 1210 (unless (%null-ptr-p filedata) 1211 (send text-view 1212 :replace-characters-in-range (ns-make-range 0 0) 1213 :with-string (make-objc-instance 1214 'ns-string 1215 :with-data filedata 1216 :encoding #$NSASCIIStringEncoding)) 1217 )))) 1218 |# 1219 1220 (define-objc-method ((:void close) lisp-editor-document) 1231 1232 (define-objc-method ((:void close) hemlock-editor-document) 1221 1233 (let* ((textstorage (slot-value self 'textstorage))) 1222 1234 (setf (slot-value self 'textstorage) (%null-ptr)) … … 1237 1249 (send textview :page-up nil))))) 1238 1250 1239 (defun hi::get-key-event (text-view ignore) 1240 (declare (ignore ignore)) 1241 (let* ((event (send (send text-view 'window) 1242 :next-event-matching-mask #$NSKeyDownMask))) 1243 (nsevent-to-key-event event))) 1251 1252 (defun hi::allocate-temporary-object-pool () 1253 (create-autorelease-pool)) 1254 1255 (defun hi::free-temporary-objects (pool) 1256 (release-autorelease-pool pool)) 1244 1257 1245 1258 (provide "COCOA-EDITOR")
Note:
See TracChangeset
for help on using the changeset viewer.
