Changeset 592


Ignore:
Timestamp:
Feb 28, 2004, 5:36:58 PM (21 years ago)
Author:
Gary Byers
Message:

Various changes. Listeners still aren't functional.

Location:
trunk/ccl/examples
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/ccl/examples/cocoa-editor.lisp

    r569 r592  
    66(eval-when (:compile-toplevel :load-toplevel :execute)
    77  (require "COCOA-WINDOW")
    8   (require "HEMLOCK-TEXTSTORAGE"))
    9 
    10 (declaim
    11   (special *open-editor-documents* *open-editor-documents-lock*)
    12   (type list *open-editor-documents*)
    13   (type lock *open-editor-documents-lock*))
    14 
    15    
    16 (defstruct cocoa-editor-info
    17   (document nil)                        ; the NSDocument
    18   (controller nil)                      ; the NSWindowController (maybe).
    19   (listener nil)                        ; True (a lisp process) if a listener
    20   (modeline-plist nil)                  ; info from attribute line
    21 )
    22 
    23            
     8  (require "HEMLOCK"))
     9
     10(eval-when (:compile-toplevel :execute)
     11  (use-interface-dir :cocoa))
     12
     13(defun make-hemlock-buffer (&rest args)
     14  (let* ((buf (apply #'hi::make-buffer args)))
     15    (or buf
     16        (progn
     17          (format t "~& couldn't make hemlock buffer with args ~s" args)
     18          (dbg)
     19          nil))))
     20         
     21;;; Define some key event modifiers.
     22
     23;;; HEMLOCK-EXT::DEFINE-CLX-MODIFIER is kind of misnamed; we can use
     24;;; it to map NSEvent modifier keys to key-event modifiers.
     25
     26(hemlock-ext::define-clx-modifier #$NSShiftKeyMask "Shift")
     27(hemlock-ext::define-clx-modifier #$NSControlKeyMask "Control")
     28(hemlock-ext::define-clx-modifier #$NSAlternateKeyMask "Meta")
     29(hemlock-ext::define-clx-modifier #$NSAlphaShiftKeyMask "Lock")
     30
     31
     32;;; We want to display a Hemlock buffer in a "pane" (an on-screen
     33;;; view) which in turn is presented in a "frame" (a Cocoa window).  A
     34;;; 1:1 mapping between frames and panes seems to fit best into
     35;;; Cocoa's document architecture, but we should try to keep the
     36;;; concepts separate (in case we come up with better UI paradigms.)
     37;;; Each pane has a modeline (which describes attributes of the
     38;;; underlying document); each frame has an echo area (which serves
     39;;; to display some commands' output and to provide multi-character
     40;;; input.)
     41
     42
     43;;; I'd pretty much concluded that it wouldn't be possible to get the
     44;;; Cocoa text system (whose storage model is based on NSString
     45;;; NSMutableAttributedString, NSTextStorage, etc.) to get along with
     46;;; Hemlock, and (since the whole point of using Hemlock was to be
     47;;; able to treat an editor buffer as a rich lisp data structure) it
     48;;; seemed like it'd be necessary to toss the higher-level Cocoa text
     49;;; system and implement our own scrolling, redisplay, selection
     50;;; ... code.
     51;;;
     52;;; Mikel Evins pointed out that NSString and friends were
     53;;; abstract classes and that there was therefore no reason (in
     54;;; theory) not to implement a thin wrapper around a Hemlock buffer
     55;;; that made it act like an NSString.  As long as the text system can
     56;;; ask a few questions about the NSString (its length and the
     57;;; character and attributes at a given location), it's willing to
     58;;; display the string in a scrolling, mouse-selectable NSTextView;
     59;;; as long as Hemlock tells the text system when and how the contents
     60;;; of the abstract string changes, Cocoa will handle the redisplay
     61;;; details.
     62;;;
     63
     64
     65
     66;;; Hemlock-buffer-string objects:
     67
     68(defclass hemlock-buffer-string (ns:ns-string)
     69    ((cache :initform nil :initarg :cache :accessor hemlock-buffer-string-cache))
     70  (:metaclass ns:+ns-object))
     71
     72;;; Cocoa wants to treat the buffer as a linear array of characters;
     73;;; Hemlock wants to treat it as a doubly-linked list of lines, so
     74;;; we often have to map between an absolute position in the buffer
     75;;; and a relative position on a line.  We can certainly do that
     76;;; by counting the characters in preceding lines every time that we're
     77;;; asked, but we're often asked to map a sequence of nearby positions
     78;;; and wind up repeating a lot of work.  Caching the results of that
     79;;; work seems to speed things up a bit in many cases; this data structure
     80;;; is used in that process.  (It's also the only way to get to the
     81;;; actual underlying Lisp buffer from inside the network of text-system
     82;;; objects.)
     83
     84(defstruct buffer-cache
     85  buffer                                ; the hemlock buffer
     86  buflen                                ; length of buffer, if known
     87  workline                              ; cache for character-at-index
     88  workline-offset                       ; cached offset of workline
     89  workline-length                       ; length of cached workline
     90  )
     91
     92;;; Initialize (or reinitialize) a buffer cache, so that it points
     93;;; to the buffer's first line (which is the only line whose
     94;;; absolute position will never change).  Code which modifies the
     95;;; buffer generally has to call this, since any cached information
     96;;; might be invalidated by the modification.
     97(defun reset-buffer-cache (d &optional (buffer (buffer-cache-buffer d)
     98                                                buffer-p))
     99  (when buffer-p (setf (buffer-cache-buffer d) buffer))
     100  (let* ((workline (hemlock::mark-line
     101                    (hemlock::buffer-start-mark buffer))))
     102    (setf (buffer-cache-buflen d) (hemlock-buffer-length buffer)
     103          (buffer-cache-workline-offset d) 0
     104          (buffer-cache-workline d) workline
     105          (buffer-cache-workline-length d) (hemlock::line-length workline))
     106    d))
     107
     108
     109;;; Update the cache so that it's describing the current absolute
     110;;; position.
     111(defun update-line-cache-for-index (cache index)
     112  (let* ((line (or
     113                (buffer-cache-workline cache)
     114                (progn
     115                  (reset-buffer-cache cache)
     116                  (buffer-cache-workline cache))))
     117         (pos (buffer-cache-workline-offset cache))
     118         (len (buffer-cache-workline-length cache))
     119         (moved nil))
     120    (loop
     121      (when (and (>= index pos)
     122                   (< index (1+ (+ pos len))))
     123          (let* ((idx (- index pos)))
     124            (when moved
     125              (setf (buffer-cache-workline cache) line
     126                    (buffer-cache-workline-offset cache) pos
     127                    (buffer-cache-workline-length cache) len))
     128            (return (values line idx))))
     129        (setq moved t)
     130      (if (< index pos)
     131        (setq line (hemlock::line-previous line)
     132              len (hemlock::line-length line)
     133              pos (1- (- pos len)))
     134        (setq line (hemlock::line-next line)
     135              pos (1+ (+ pos len))
     136              len (hemlock::line-length line))))))
     137
     138;;; Ask Hemlock to count the characters in the buffer.
     139(defun hemlock-buffer-length (buffer)
     140  (hemlock::count-characters (hemlock::buffer-region buffer)))
     141
     142;;; Find the line containing (or immediately preceding) index, which is
     143;;; assumed to be less than the buffer's length.  Return the character
     144;;; in that line or the trailing #\newline, as appropriate.
     145(defun hemlock-char-at-index (cache index)
     146  (multiple-value-bind (line idx) (update-line-cache-for-index cache index)
     147    (let* ((len (hemlock::line-length line)))
     148      (if (< idx len)
     149        (hemlock::line-character line idx)
     150        #\newline))))
     151
     152;;; Given an absolute position, move the specified mark to the appropriate
     153;;; offset on the appropriate line.
     154(defun move-hemlock-mark-to-absolute-position (mark cache abspos)
     155  (multiple-value-bind (line idx) (update-line-cache-for-index cache abspos)
     156    (hemlock::move-to-position mark idx line)))
     157
     158;;; Return the absolute position of the mark in the containing buffer.
     159;;; This doesn't use the caching mechanism, so it's always linear in the
     160;;; number of preceding lines.
     161(defun mark-absolute-position (mark)
     162  (let* ((pos (hemlock::mark-charpos mark)))
     163    (do* ((line (hemlock::line-previous (hemlock::mark-line mark))
     164                (hemlock::line-previous line)))
     165         ((null line) pos)
     166      (incf pos (1+ (hemlock::line-length line))))))
     167
     168;;; Return the length of the abstract string, i.e., the number of
     169;;; characters in the buffer (including implicit newlines.)
     170(define-objc-method ((:unsigned length)
     171                     hemlock-buffer-string)
     172  (let* ((cache (hemlock-buffer-string-cache self)))
     173      (or (buffer-cache-buflen cache)
     174          (setf (buffer-cache-buflen cache)
     175                (hemlock-buffer-length (buffer-cache-buffer cache))))))
     176
     177
     178;;; Return the character at the specified index (as a :unichar.)
     179(define-objc-method ((:unichar :character-at-index (unsigned index))
     180                     hemlock-buffer-string)
     181  (char-code (hemlock-char-at-index (hemlock-buffer-string-cache self) index)))
     182
     183
     184;;; Return an NSData object representing the bytes in the string.  If
     185;;; the underlying buffer uses #\linefeed as a line terminator, we can
     186;;; let the superclass method do the work; otherwise, we have to
     187;;; ensure that each line is terminated according to the buffer's
     188;;; conventions.
     189(define-objc-method ((:id :data-using-encoding (:<NSS>tring<E>ncoding encoding)
     190                          :allow-lossy-conversion (:<BOOL> flag))
     191                     hemlock-buffer-string)
     192  (let* ((buffer (buffer-cache-buffer (hemlock-buffer-string-cache self)))
     193         (external-format (if buffer (hi::buffer-external-format buffer )))
     194         (raw-length (if buffer (hemlock-buffer-length buffer) 0)))
     195    (if (eql 0 raw-length)
     196      (make-objc-instance 'ns:ns-mutable-data :with-length 0)
     197      (case external-format
     198        ((:unix nil)
     199         (send-super :data-using-encoding encoding :allow-lossy-conversion flag))
     200        ((:macos :cp/m)
     201         (let* ((cp/m-p (eq external-format :cp/m)))
     202           (when cp/m-p
     203         ;; This may seem like lot of fuss about an ancient OS and its
     204         ;; odd line-termination conventions.  Of course, I'm actually
     205         ;; referring to CP/M-86.
     206             (do* ((line (hi::mark-line (hi::buffer-start-mark buffer))
     207                         next)
     208                   (next (hi::line-next line) (hi::line-next line)))
     209                  ((null line))
     210               (when next (incf raw-length))))
     211           (let* ((pos 0)
     212                  (data (make-objc-instance 'ns:ns-mutable-data
     213                                            :with-length raw-length))
     214                  (bytes (send data 'mutable-bytes)))
     215             (do* ((line (hi::mark-line (hi::buffer-start-mark buffer))
     216                         next)
     217                   (next (hi::line-next line) (hi::line-next line)))
     218                  ((null line) data)
     219               (let* ((chars (hi::line-chars line))
     220                      (len (length chars)))
     221                 (unless (zerop len)
     222                   (%copy-ivector-to-ptr chars 0 bytes pos len)
     223                   (incf pos len))
     224                 (when next
     225                   (setf (%get-byte bytes pos) (char-code #\return))
     226                   (when cp/m-p
     227                     (incf pos)
     228                   (setf (%get-byte bytes pos) (char-code #\linefeed)) 
     229                   (incf pos))))))))))))
     230
     231
     232;;; For debugging, mostly: make the printed representation of the string
     233;;; referenence the named Hemlock buffer.
     234(define-objc-method ((:id description)
     235                     hemlock-buffer-string)
     236  (let* ((cache (hemlock-buffer-string-cache self))
     237         (b (buffer-cache-buffer cache)))
     238    (with-cstrs ((s (format nil "~a" b)))
     239      (send (@class ns-string) :string-with-format #@"<%s for %s>"
     240        (:address (#_object_getClassName self) :address s)))))
     241
     242
     243
     244
     245;;; Lisp-text-storage objects
     246(defclass lisp-text-storage (ns:ns-text-storage)
     247    ((string :foreign-type :id)
     248     (defaultattrs :foreign-type :id))
     249  (:metaclass ns:+ns-object))
     250
     251;;; Access the string.  It'd be nice if this was a generic function;
     252;;; we could have just made a reader method in the class definition.
     253(define-objc-method ((:id string) lisp-text-storage)
     254  (slot-value self 'string))
     255
     256(define-objc-method ((:id :init-with-string s) lisp-text-storage)
     257  (let* ((newself (send-super 'init)))
     258    (setf (slot-value newself 'string) s
     259          (slot-value newself 'defaultattrs) (create-text-attributes))
     260    newself))
     261
     262;;; This is the only thing that's actually called to create a
     263;;; lisp-text-storage object.  (It also creates the underlying
     264;;; hemlock-buffer-string.)
     265(defun make-textstorage-for-hemlock-buffer (buffer)
     266  (make-objc-instance 'lisp-text-storage
     267                      :with-string
     268                      (make-instance
     269                       'hemlock-buffer-string
     270                       :cache
     271                       (reset-buffer-cache
     272                        (make-buffer-cache)
     273                        buffer))))
     274
     275;;; So far, we're ignoring Hemlock's font-marks, so all characters in
     276;;; the buffer are presumed to have default attributes.
     277(define-objc-method ((:id :attributes-at-index (:unsigned index)
     278                          :effective-range ((* :<NSR>ange) rangeptr))
     279                     lisp-text-storage)
     280  (declare (ignorable index))
     281  (let* ((buffer-cache (hemlock-buffer-string-cache (slot-value self 'string)))
     282         (len (buffer-cache-buflen buffer-cache)))
     283    (unless (%null-ptr-p rangeptr)
     284      (setf (pref rangeptr :<NSR>ange.location) 0
     285            (pref rangeptr :<NSR>ange.length) len))
     286    (slot-value self 'defaultattrs)))
     287
     288;;; The range's origin should probably be the buffer's point; if
     289;;; the range has non-zero length, we probably need to think about
     290;;; things harder.
     291(define-objc-method ((:void :replace-characters-in-range (:<NSR>ange r)
     292                            :with-string string)
     293                     lisp-text-storage)
     294  (#_NSLog #@"replace-characters-in-range (%d %d) with-string %@"
     295           :unsigned (pref r :<NSR>ange.location)
     296           :unsigned (pref r :<NSR>ange.length)
     297           :id string))
     298
     299;;; I'm not sure if we want the text system to be able to change
     300;;; attributes in the buffer.
     301(define-objc-method ((:void :set-attributes attributes
     302                            :range (:<NSR>ange r))
     303                     lisp-text-storage)
     304  (#_NSLog #@"set-attributes %@ range (%d %d)"
     305           :id attributes
     306           :unsigned (pref r :<NSR>ange.location)
     307           :unsigned (pref r :<NSR>ange.length)))
     308
     309
     310;;; Again, it's helpful to see the buffer name when debugging.
     311(define-objc-method ((:id description)
     312                     lisp-text-storage)
     313  (send (@class ns-string) :string-with-format #@"%s : string %@"
     314        (:address (#_object_getClassName self) :id (slot-value self 'string))))
     315
     316(defun close-hemlock-textstorage (ts)
     317  (let* ((string (slot-value ts 'string)))
     318    (setf (slot-value ts 'string) (%null-ptr))
     319    (unless (%null-ptr-p string)
     320      (let* ((cache (hemlock-buffer-string-cache string))
     321             (buffer (if cache (buffer-cache-buffer cache))))
     322        (when buffer
     323          (setf (buffer-cache-buffer cache) nil
     324                (slot-value string 'cache) nil
     325                (hi::buffer-document buffer) nil)
     326          (let* ((p (hi::buffer-process buffer)))
     327            (when p
     328              (setf (hi::buffer-process buffer) nil)
     329              (process-kill p)))
     330          (when (eq buffer hi::*current-buffer*)
     331            (setf (hi::current-buffer)
     332                  (car (last hi::*buffer-list*))))
     333          (hi::invoke-hook (hi::buffer-delete-hook buffer) buffer)
     334          (hi::invoke-hook hemlock::delete-buffer-hook buffer)
     335          (setq hi::*buffer-list* (delq buffer hi::*buffer-list*))
     336          (hi::delete-string (hi::buffer-name buffer) hi::*buffer-names*))))))
     337
     338     
     339
     340
     341
     342
     343;;; A specialized NSTextView.  Some of the instance variables are intended
     344;;; to support paren highlighting by blinking, but that doesn't work yet.
     345;;; The NSTextView is part of the "pane" object that displays buffers.
     346(defclass hemlock-text-view (ns:ns-text-view)
     347    ((timer :foreign-type :id :accessor blink-timer)
     348     (blink-pos :foreign-type :int :accessor blink-pos)
     349     (blink-phase :foreign-type :<BOOL> :accessor blink-phase)
     350     (blink-char :foreign-type :int :accessor blink-char)
     351     (pane :foreign-type :id :accessor text-view-pane))
     352  (:metaclass ns:+ns-object))
     353
     354;;; Access the underlying buffer in one swell foop.
     355(defmethod text-view-buffer ((self hemlock-text-view))
     356  (buffer-cache-buffer (hemlock-buffer-string-cache (send (send self 'text-storage) 'string))))
     357
     358;;; Translate a keyDown NSEvent to a Hemlock key-event.
     359(defun nsevent-to-key-event (nsevent)
     360  (let* ((unmodchars (send nsevent 'characters-ignoring-modifiers))
     361         (n (if (%null-ptr-p unmodchars)
     362              0
     363              (send unmodchars 'length)))
     364         (c (if (eql n 1)
     365              (send unmodchars :character-at-index 0))))
     366    (when c
     367      (let* ((bits 0)
     368             (modifiers (send nsevent 'modifier-flags))
     369             (useful-modifiers (logandc2 modifiers
     370                                         (logior #$NSShiftKeyMask
     371                                                 #$NSAlphaShiftKeyMask))))
     372        (dolist (map hemlock-ext::*modifier-translations*)
     373          (when (logtest useful-modifiers (car map))
     374            (setq bits (logior bits (hemlock-ext::key-event-modifier-mask
     375                                     (cdr map))))))
     376        (hemlock-ext::make-key-event c bits)))))
     377
     378;;; Process a key-down NSEvent in a lisp text view by translating it
     379;;; into a Hemlock key event and passing it into the Hemlock command
     380;;; interpreter.  The underlying buffer becomes Hemlock's current buffer
     381;;; and the containing pane becomes Hemlock's current window when the
     382;;; command is processed.  Use the frame's command state object.
     383
     384(define-objc-method ((:void :key-down event)
     385                     hemlock-text-view)
     386  #+debug
     387  (#_NSLog #@"Key down event = %@" :address event)
     388  (let* ((buffer (text-view-buffer self)))
     389    (when buffer
     390      (let* ((info (hemlock-frame-command-info (send self 'window))))
     391        (when info
     392          (let* ((key-event (nsevent-to-key-event event)))
     393            (when event
     394              (unless (eq buffer hi::*current-buffer*)
     395                (setf (hi::current-buffer) buffer))
     396              (let* ((pane (text-view-pane self)))
     397                (unless (eql pane (hi::current-window))
     398                  (setf (hi::current-window) pane)))
     399              #+debug
     400              (format t "~& key-event = ~s" key-event)
     401              (hi::interpret-key-event key-event info))))))))
     402
     403;;; Update the underlying buffer's point.  Should really set the
     404;;; active region (in Hemlock terms) as well.
     405(define-objc-method ((:void :set-selected-range (:<NSR>ange r)
     406                            :affinity (:<NSS>election<A>ffinity affinity)
     407                            :still-selecting (:<BOOL> still-selecting))
     408                     hemlock-text-view)
     409  (let* ((d (hemlock-buffer-string-cache (send self 'string)))
     410         (point (hemlock::buffer-point (buffer-cache-buffer d)))
     411         (location (pref r :<NSR>ange.location))
     412         (len (pref r :<NSR>ange.length)))
     413    (when (eql len 0)
     414      (move-hemlock-mark-to-absolute-position point d location))
     415    (send-super :set-selected-range r
     416                :affinity affinity
     417                :still-selecting still-selecting)))
     418
     419
     420
     421
     422;;; Modeline-view
     423
     424;;; The modeline view is embedded in the horizontal scroll bar of the
     425;;; scrollview which surrounds the textview in a pane.  (A view embedded
     426;;; in a scrollbar like this is sometimes called a "placard").  Whenever
     427;;; the view's invalidated, its drawRect: method draws a string containing
     428;;; the current values of the buffer's modeline fields.
     429
     430(defclass modeline-view (ns:ns-view)
     431    ((pane :foreign-type :id :accessor modeline-view-pane))
     432  (:metaclass ns:+ns-object))
     433
     434
     435;;; Attributes to use when drawing the modeline fields.  There's no
     436;;; simple way to make the "placard" taller, so using fonts larger than
     437;;; about 12pt probably wouldn't look too good.  10pt Courier's a little
     438;;; small, but allows us to see more of the modeline fields (like the
     439;;; full pathname) in more cases.
     440(defloadvar *modeline-text-attributes* nil)
     441(defparameter *modeline-font-name* "Courier New Bold Italic")
     442(defparameter *modeline-font-size* 10.0)
     443
     444
     445;;; Find the underlying buffer.
     446(defun buffer-for-modeline-view (mv)
     447  (let* ((pane (modeline-view-pane mv)))
     448    (unless (%null-ptr-p pane)
     449      (let* ((tv (text-pane-text-view pane)))
     450        (unless (%null-ptr-p tv)
     451          (text-view-buffer tv))))))
     452
     453;;; Draw a string in the modeline view.  The font and other attributes
     454;;; are initialized lazily; apparently, calling the Font Manager too
     455;;; early in the loading sequence confuses some Carbon libraries that're
     456;;; used in the event dispatch mechanism,
     457(defun draw-modeline-string (modeline-view)
     458  (let* ((pane (modeline-view-pane modeline-view))
     459         (buffer (buffer-for-modeline-view modeline-view)))
     460    (when buffer
     461      ;; You don't want to know why this is done this way.
     462      (unless *modeline-text-attributes*
     463        (setq *modeline-text-attributes*
     464              (create-text-attributes :color (send (@class "NSColor") 'black-color)
     465                                      :font (default-font
     466                                              :name *modeline-font-name*
     467                                              :size *modeline-font-size*))))
     468     
     469      (let* ((string
     470              (apply #'concatenate 'string
     471                     (mapcar
     472                      #'(lambda (field)
     473                          (funcall (hi::modeline-field-function field)
     474                                   buffer pane))
     475                      (hi::buffer-modeline-fields buffer)))))
     476        (send (%make-nsstring string)
     477              :draw-at-point (ns-make-point 0.0f0 0.0f0)
     478              :with-attributes *modeline-text-attributes*)))))
     479
     480;;; Draw the underlying buffer's modeline string on a white background
     481;;; with a bezeled border around it.
     482(define-objc-method ((:void :draw-rect (:<NSR>ect rect))
     483                     modeline-view)
     484  (declare (ignore rect))
     485  (slet ((frame (send self 'bounds)))
     486     (#_NSDrawWhiteBezel frame frame)
     487     (draw-modeline-string self)))
     488
     489;;; Hook things up so that the modeline is updated whenever certain buffer
     490;;; attributes change.
     491(hi::%init-redisplay)
     492
     493
     494
     495;;; Modeline-scroll-view
     496
     497;;; This is just an NSScrollView that draws a "placard" view (the modeline)
     498;;; in the horizontal scrollbar.  The modeline's arbitrarily given the
     499;;; leftmost 75% of the available real estate.
     500(defclass modeline-scroll-view (ns:ns-scroll-view)
     501    ((modeline :foreign-type :id :accessor scroll-view-modeline)
     502     (pane :foreign-type :id :accessor scroll-view-pane))
     503  (:metaclass ns:+ns-object))
     504
     505;;; Making an instance of a modeline scroll view instantiates the
     506;;; modeline view, as well.
     507
     508(define-objc-method ((:id :init-with-frame (:<NSR>ect frame))
     509                     modeline-scroll-view)
     510    (let* ((v (send-super :init-with-frame frame)))
     511      (when v
     512        (let* ((modeline (make-objc-instance 'modeline-view)))
     513          (send v :add-subview modeline)
     514          (setf (scroll-view-modeline v) modeline)))
     515      v))
     516
     517;;; Scroll views use the "tile" method to lay out their subviews.
     518;;; After the next-method has done so, steal some room in the horizontal
     519;;; scroll bar and place the modeline view there.
     520
     521(define-objc-method ((:void tile) modeline-scroll-view)
     522  (send-super 'tile)
     523  (let* ((modeline (scroll-view-modeline self)))
     524    (when (and (send self 'has-horizontal-scroller)
     525               (not (%null-ptr-p modeline)))
     526      (let* ((hscroll (send self 'horizontal-scroller)))
     527        (slet ((scrollbar-frame (send hscroll 'frame))
     528               (modeline-frame (send hscroll 'frame))) ; sic
     529           (let* ((modeline-width (* (pref modeline-frame
     530                                           :<NSR>ect.size.width)
     531                                     0.75e0)))
     532             (declare (single-float modeline-width))
     533             (setf (pref modeline-frame :<NSR>ect.size.width)
     534                   modeline-width
     535                   (the single-float
     536                     (pref scrollbar-frame :<NSR>ect.size.width))
     537                   (- (the single-float
     538                        (pref scrollbar-frame :<NSR>ect.size.width))
     539                      modeline-width)
     540                   (the single-float
     541                     (pref scrollbar-frame :<NSR>ect.origin.x))
     542                   (+ (the single-float
     543                        (pref scrollbar-frame :<NSR>ect.origin.x))
     544                      modeline-width))
     545             (send hscroll :set-frame scrollbar-frame)
     546             (send modeline :set-frame modeline-frame)))))))
     547
     548
     549
     550;;; Text-pane
     551
     552;;; The text pane is just an NSBox that (a) provides a draggable border
     553;;; around (b) encapsulates the text view and the mode line.
     554
     555(defclass text-pane (ns:ns-box)
     556    ((text-view :foreign-type :id :accessor text-pane-text-view)
     557     (mode-line :foreign-type :id :accessor text-pane-mode-line)
     558     (scroll-view :foreign-type :id :accessor text-pane-scroll-view))
     559  (:metaclass ns:+ns-object))
     560
     561;;; Mark the pane's modeline as needing display.  This is called whenever
     562;;; "interesting" attributes of a buffer are changed.
     563
     564(defun hi::invalidate-modeline (pane)
     565  (send (text-pane-mode-line pane) :set-needs-display t))
     566
     567(define-objc-method ((:id :init-with-frame (:<NSR>ect frame))
     568                     text-pane)
     569    (let* ((pane (send-super :init-with-frame frame)))
     570      (unless (%null-ptr-p pane)
     571        (send pane :set-autoresizing-mask (logior
     572                                           #$NSViewWidthSizable
     573                                           #$NSViewHeightSizable))
     574        (send pane :set-box-type #$NSBoxPrimary)
     575        (send pane :set-border-type #$NSLineBorder)
     576        (send pane :set-title-position #$NSNoTitle))
     577      pane))
     578
     579
     580(defun make-scrolling-text-view-for-textstorage (textstorage x y width height)
     581  (slet ((contentrect (ns-make-rect x y width height)))
     582    (let* ((scrollview (send (make-objc-instance
     583                              'modeline-scroll-view
     584                              :with-frame contentrect) 'autorelease)))
     585      (send scrollview :set-border-type #$NSBezelBorder)
     586      (send scrollview :set-has-vertical-scroller t)
     587      (send scrollview :set-has-horizontal-scroller t)
     588      (send scrollview :set-rulers-visible nil)
     589      (send scrollview :set-autoresizing-mask (logior
     590                                               #$NSViewWidthSizable
     591                                               #$NSViewHeightSizable))
     592      (send (send scrollview 'content-view) :set-autoresizes-subviews t)
     593      (let* ((layout (make-objc-instance 'ns-layout-manager)))
     594        (send textstorage :add-layout-manager layout)
     595        (send layout 'release)
     596        (slet* ((contentsize (send scrollview 'content-size))
     597                (containersize (ns-make-size
     598                                1.0f7
     599                                1.0f7))
     600                (tv-frame (ns-make-rect
     601                           0.0f0
     602                           0.0f0
     603                           (pref contentsize :<NSS>ize.width)
     604                           (pref contentsize :<NSS>ize.height))))
     605          (let* ((container (send (make-objc-instance
     606                                   'ns-text-container
     607                                   :with-container-size containersize)
     608                                  'autorelease)))
     609            (send layout :add-text-container container)
     610            (let* ((tv (send (make-objc-instance 'hemlock-text-view
     611                                                 :with-frame tv-frame
     612                                                 :text-container container)
     613                             'autorelease)))
     614              (send tv :set-min-size (ns-make-size
     615                                      0.0f0
     616                                      (pref contentsize :<NSS>ize.height)))
     617              (send tv :set-max-size (ns-make-size 1.0f7 1.0f7))
     618              (send tv :set-rich-text nil)
     619              (send tv :set-horizontally-resizable t)
     620              (send tv :set-vertically-resizable t)
     621              (send tv :set-autoresizing-mask #$NSViewWidthSizable)
     622              (send container :set-width-tracks-text-view nil)
     623              (send container :set-height-tracks-text-view nil)
     624              (send scrollview :set-document-view tv)         
     625              (values tv scrollview))))))))
     626
     627(defun make-scrolling-textview-for-pane (pane textstorage)
     628  (slet ((contentrect (send (send pane 'content-view) 'frame)))
     629    (multiple-value-bind (tv scrollview)
     630        (make-scrolling-text-view-for-textstorage
     631         textstorage
     632         (pref contentrect :<NSR>ect.origin.x)
     633         (pref contentrect :<NSR>ect.origin.y)
     634         (pref contentrect :<NSR>ect.size.width)
     635         (pref contentrect :<NSR>ect.size.height))
     636      (send pane :set-content-view scrollview)
     637      (setf (slot-value pane 'scroll-view) scrollview
     638            (slot-value pane 'text-view) tv
     639            (slot-value tv 'pane) pane
     640            (slot-value scrollview 'pane) pane)
     641      (let* ((modeline  (scroll-view-modeline scrollview)))
     642        (setf (slot-value pane 'mode-line) modeline
     643              (slot-value modeline 'pane) pane))
     644      tv)))
     645
     646
     647(defmethod hemlock-frame-command-info ((w ns:ns-window))
     648  nil)
     649
     650
     651(defclass hemlock-frame (ns:ns-window)
     652    ((command-info :initform (hi::make-command-interpreter-info)
     653                   :accessor hemlock-frame-command-info))
     654  (:metaclass ns:+ns-object))
     655
     656
     657(defmethod shared-initialize :after ((w hemlock-frame)
     658                                     slot-names
     659                                     &key &allow-other-keys)
     660  (declare (ignore slot-names))
     661  (let ((info (hemlock-frame-command-info w)))
     662    (when info
     663      (setf (hi::command-interpreter-info-frame info) w))))
     664
     665
     666(defun get-cocoa-window-flag (w flagname)
     667  (case flagname
     668    (:accepts-mouse-moved-events
     669     (send w 'accepts-mouse-moved-events))
     670    (:cursor-rects-enabled
     671     (send w 'are-cursor-rects-enabled))
     672    (:auto-display
     673     (send w 'is-autodisplay))))
     674
     675
     676
     677(defun (setf get-cocoa-window-flag) (value w flagname)
     678  (case flagname
     679    (:accepts-mouse-moved-events
     680     (send w :set-accepts-mouse-moved-events value))
     681    (:auto-display
     682     (send w :set-autodisplay value))))
     683
     684
     685
     686(defun activate-window (w)
     687  ;; Make w the "key" and frontmost window.  Make it visible, if need be.
     688  (send w :make-key-and-order-front nil))
     689
     690(defun new-hemlock-document-window (&key
     691                                    (x 200.0)
     692                                    (y 200.0)
     693                                    (height 200.0)
     694                                    (width 500.0)
     695                                    (closable t)
     696                                    (iconifyable t)
     697                                    (metal t)
     698                                    (expandable t)
     699                                    (backing :buffered)
     700                                    (defer nil)
     701                                    (accepts-mouse-moved-events nil)
     702                                    (auto-display t)
     703                                    (activate t))
     704  (rlet ((frame :<NSR>ect :origin.x (float x) :origin.y (float y) :size.width (float width) :size.height (float height)))
     705    (let* ((stylemask
     706            (logior #$NSTitledWindowMask
     707                    (if closable #$NSClosableWindowMask 0)
     708                    (if iconifyable #$NSMiniaturizableWindowMask 0)
     709                    (if expandable #$NSResizableWindowMask 0)
     710                    (if metal #$NSTexturedBackgroundWindowMask 0)))
     711           (backing-type
     712            (ecase backing
     713              ((t :retained) #$NSBackingStoreRetained)
     714              ((nil :nonretained) #$NSBackingStoreNonretained)
     715              (:buffered #$NSBackingStoreBuffered)))
     716           (w (make-instance
     717               'hemlock-frame
     718               :with-content-rect frame
     719               :style-mask stylemask
     720               :backing backing-type
     721               :defer defer)))
     722      (setf (get-cocoa-window-flag w :accepts-mouse-moved-events)
     723            accepts-mouse-moved-events
     724            (get-cocoa-window-flag w :auto-display)
     725            auto-display)
     726      (when activate (activate-window w))
     727      (values w (add-pane-to-window w :reserve-below 20.0)))))
     728
     729
     730
     731(defun add-pane-to-window (w &key (reserve-above 0.0f0) (reserve-below 0.0f0))
     732  (let* ((window-content-view (send w 'content-view)))
     733    (slet ((window-frame (send window-content-view 'frame)))
     734      (slet ((pane-rect (ns-make-rect 0.0f0
     735                                      reserve-below
     736                                      (pref window-frame :<NSR>ect.size.width)
     737                                      (- (pref window-frame :<NSR>ect.size.height) (+ reserve-above reserve-below)))))
     738        (let* ((pane (make-objc-instance 'text-pane :with-frame pane-rect)))
     739          (send window-content-view :add-subview pane)
     740          pane)))))
     741
     742
     743         
     744                                       
     745                                     
     746(defun textpane-for-textstorage (ts)
     747  (let* ((pane (nth-value
     748                1
     749                (new-hemlock-document-window :activate nil)))
     750         (tv (make-scrolling-textview-for-pane pane ts)))
     751    (multiple-value-bind (height width)
     752        (size-of-char-in-font (default-font))
     753      (size-textview-containers tv height width 24 80))
     754    pane))
     755
     756
     757(defun read-file-to-hemlock-buffer (path)
     758  (hemlock::find-file-buffer path))
     759
     760(defun hemlock-buffer-from-nsstring (nsstring name &rest modes)
     761  (let* ((buffer (make-hemlock-buffer name :modes modes)))
     762    (nsstring-to-buffer nsstring buffer)))
     763
     764(defun nsstring-to-buffer (nsstring buffer)
     765  (let* ((document (hi::buffer-document buffer)))
     766    (setf (hi::buffer-document buffer) nil)
     767    (unwind-protect
     768         (progn
     769           (hi::delete-region (hi::buffer-region buffer))
     770           (hi::modifying-buffer buffer)
     771           (hi::with-mark ((mark (hi::buffer-point buffer) :left-inserting))
     772             (let* ((string-len (send nsstring 'length))
     773                    (line-start 0)
     774                    (first-line-terminator ())
     775                    (first-line (hi::mark-line mark))
     776                    (previous first-line)
     777                    (buffer (hi::line-%buffer first-line)))
     778               (slet ((remaining-range (ns-make-range 0 1)))
     779                 (rlet ((line-end-index :unsigned)
     780                        (contents-end-index :unsigned))
     781                   (do* ((number (+ (hi::line-number first-line) hi::line-increment)
     782                                 (+ number hi::line-increment)))
     783                        ((= line-start string-len)
     784                         (let* ((line (hi::mark-line mark)))
     785                           (hi::insert-string mark (make-string 0))
     786                           (setf (hi::line-next previous) line
     787                                 (hi::line-previous line) previous))
     788                         nil)
     789                     (setf (pref remaining-range :<NSR>ange.location) line-start)
     790                     (send nsstring
     791                           :get-line-start (%null-ptr)
     792                           :end line-end-index
     793                           :contents-end contents-end-index
     794                           :for-range remaining-range)
     795                     (let* ((contents-end (pref contents-end-index :unsigned))
     796                            (line-end (pref line-end-index :unsigned))
     797                            (chars (make-string (- contents-end line-start))))
     798                       (do* ((i line-start (1+ i))
     799                             (j 0 (1+ j)))
     800                            ((= i contents-end))
     801                         (setf (schar chars j) (code-char (send nsstring :character-at-index i))))
     802                       (unless first-line-terminator
     803                         (let* ((terminator (code-char
     804                                             (send nsstring :character-at-index
     805                                                   contents-end))))
     806                           (setq first-line-terminator
     807                                 (case terminator
     808                                   (#\return (if (= line-end (+ contents-end 2))
     809                                               :cp/m
     810                                               :macos))
     811                                   (t :unix)))))
     812                       (if (eq previous first-line)
     813                         (progn
     814                           (hi::insert-string mark chars)
     815                           (hi::insert-character mark #\newline)
     816                           (setq first-line nil))
     817                         (if (eq string-len contents-end)
     818                           (hi::insert-string mark chars)
     819                           (let* ((line (hi::make-line
     820                                         :previous previous
     821                                         :%buffer buffer
     822                                         :chars chars
     823                                         :number number)))
     824                             (setf (hi::line-next previous) line)
     825                             (setq previous line))))
     826                       (setq line-start line-end)))))
     827               (when first-line-terminator
     828                 (setf (hi::buffer-external-format buffer) first-line-terminator))))
     829           (setf (hi::buffer-modified buffer) nil)
     830           (hi::buffer-start (hi::buffer-point buffer))
     831           buffer)
     832      (setf (hi::buffer-document buffer) document))))
     833
     834(setq hi::*beep-function* #'(lambda (stream)
     835                              (declare (ignore stream))
     836                              (#_NSBeep)))
     837
     838
     839;;; This function must run in the main event thread.
     840(defun %hemlock-frame-for-textstorage (ts title activate)
     841  (let* ((pane (textpane-for-textstorage ts))
     842         (w (send pane 'window)))
     843    (when title (send w :set-title (%make-nsstring title)))
     844    (when activate (activate-window w))
     845    w))
     846
     847(defun hemlock-frame-for-textstorage (ts title activate)
     848  (process-interrupt *cocoa-event-process*
     849                     #'%hemlock-frame-for-textstorage
     850                     ts title activate))
     851
     852
     853(defun for-each-textview-using-storage (textstorage f)
     854  (let* ((layouts (send textstorage 'layout-managers)))
     855    (unless (%null-ptr-p layouts)
     856      (dotimes (i (send layouts 'count))
     857        (let* ((layout (send layouts :object-at-index i))
     858               (containers (send layout 'text-containers)))
     859          (unless (%null-ptr-p containers)
     860            (dotimes (j (send containers 'count))
     861              (let* ((container (send containers :object-at-index j))
     862                     (tv (send container 'text-view)))
     863                (funcall f tv)))))))))
     864
     865
     866 
     867(defun hi::document-begin-editing (document)
     868  (send (slot-value document 'textstorage) 'begin-editing))
     869
     870(defun hi::document-end-editing (document)
     871  (send (slot-value document 'textstorage) 'end-editing))
     872
     873(defun hi::document-set-point-position (document)
     874  (let* ((textstorage (slot-value document 'textstorage))
     875         (string (send textstorage 'string))
     876         (buffer (buffer-cache-buffer (hemlock-buffer-string-cache string)))
     877         (point (hi::buffer-point buffer))
     878         (pos (mark-absolute-position point)))
     879    (for-each-textview-using-storage
     880     textstorage
     881     #'(lambda (tv)
     882         (slet ((selection (ns-make-range pos 0)))
     883          (send tv :set-selected-range selection)
     884          (send tv :scroll-range-to-visible selection))))))
     885
     886
     887(defun textstorage-note-insertion-at-position (textstorage pos n)
     888  (send textstorage
     889        :edited #$NSTextStorageEditedAttributes
     890        :range (ns-make-range pos 0)
     891        :change-in-length n)
     892  (send textstorage
     893        :edited #$NSTextStorageEditedCharacters
     894        :range (ns-make-range pos n)
     895        :change-in-length 0))
     896
     897(defun hi::buffer-note-insertion (buffer mark n)
     898  (when (hi::bufferp buffer)
     899    (let* ((document (hi::buffer-document buffer))
     900           (textstorage (if document (slot-value document 'textstorage))))
     901      (when textstorage
     902        (let* ((pos (mark-absolute-position mark)))
     903          (unless (eq (hi::mark-%kind mark) :right-inserting)
     904            (decf pos n))
     905          #+debug 0
     906          (format t "~&pos = ~d, n = ~d" pos n)
     907          (let* ((display (hemlock-buffer-string-cache (send textstorage 'string))))
     908            (reset-buffer-cache display)
     909            (update-line-cache-for-index display pos))
     910          (textstorage-note-insertion-at-position textstorage pos n))))))
     911
     912 
     913
     914(defun hi::buffer-note-deletion (buffer mark n)
     915  (when (hi::bufferp buffer)
     916    (let* ((document (hi::buffer-document buffer))
     917           (textstorage (if document (slot-value document 'textstorage))))
     918      (when textstorage
     919        (let* ((pos (mark-absolute-position mark)))
     920          (setq n (abs n))
     921          (let* ((cache (hemlock-buffer-string-cache (send textstorage 'string))))
     922            (reset-buffer-cache cache)
     923            (update-line-cache-for-index cache pos))
     924
     925          (send textstorage
     926                :edited #$NSTextStorageEditedAttributes
     927                :range (ns-make-range pos n)
     928                :change-in-length (- n)))))))
     929
     930(defun hi::set-document-modified (document flag)
     931  (send document
     932        :update-change-count (if flag #$NSChangeDone #$NSChangeCleared)))
     933
     934
     935(defun hi::document-panes (document)
     936  (let* ((ts (slot-value document 'textstorage))
     937         (panes ()))
     938    (for-each-textview-using-storage
     939     ts
     940     #'(lambda (tv)
     941         (let* ((pane (text-view-pane tv)))
     942           (unless (%null-ptr-p pane)
     943             (push pane panes)))))
     944    panes))
     945
    24946   
    25947
     
    62984          (send window :set-resize-increments
    63985                (ns-make-size char-width char-height)))))))
    64      
    65 (defun info-from-document (doc)
    66   (with-lock-grabbed (*open-editor-documents-lock*)
    67     (find doc *open-editor-documents* :key #'cocoa-editor-info-document)))
    68 
    69 (defun info-from-controller (controller)
    70   (with-lock-grabbed (*open-editor-documents-lock*)
    71     (find controller *open-editor-documents* :key #'cocoa-editor-info-controller)))
    72 
    73 
    74 
    75986                                   
    76987 
     
    961007  (let* ((doc (send-super 'init)))
    971008    (unless (%null-ptr-p doc)
    98       (let* ((buffer (hi::make-buffer
     1009      (let* ((buffer (make-hemlock-buffer
    991010                      (lisp-string-from-nsstring (send doc 'display-name))
    1001011                      :modes '("Lisp"))))
    1011012        (setf (slot-value doc 'textstorage)
    102               (make-textstorage-for-hemlock-buffer
    103                buffer)
     1013              (make-textstorage-for-hemlock-buffer buffer)
    1041014              (hi::buffer-document buffer) doc)))
    1051015    doc))
     
    1121022  (let* ((pathname (lisp-string-from-nsstring filename))
    1131023         (buffer-name (hi::pathname-to-buffer-name pathname))
    114          (buffer (hi::make-buffer buffer-name))
     1024         (buffer (or
     1025                  (hemlock-document-buffer self)
     1026                  (let* ((b (make-hemlock-buffer buffer-name)))
     1027                    (setf (hi::buffer-pathname b) pathname)
     1028                    (setf (slot-value self 'textstorage)
     1029                          (make-textstorage-for-hemlock-buffer b))
     1030                    b)))
    1151031         (data (make-objc-instance 'ns:ns-data
    1161032                                   :with-contents-of-file filename))
    1171033         (string (make-objc-instance 'ns:ns-string
    1181034                                     :with-data data
    119                                      :encoding #$NSMacOSRomanStringEncoding)))
    120     (setf (hi::buffer-pathname buffer) pathname)
     1035                                     :encoding #$NSASCIIStringEncoding)))
     1036    (hi::document-begin-editing self)
    1211037    (nsstring-to-buffer string buffer)
    122     (hi::buffer-start (hi::buffer-point buffer))
     1038    (let* ((textstorage (slot-value self 'textstorage))
     1039           (display (hemlock-buffer-string-cache (send textstorage 'string))))
     1040      (reset-buffer-cache display)
     1041      (update-line-cache-for-index display 0)
     1042      (textstorage-note-insertion-at-position
     1043       textstorage
     1044       0
     1045       (hemlock-buffer-length buffer)))
     1046    (hi::document-end-editing self)
    1231047    (setf (hi::buffer-modified buffer) nil)
    1241048    (hi::process-file-options buffer pathname)
    125     (setf (slot-value self 'textstorage)
    126           (make-textstorage-for-hemlock-buffer buffer)
    127           (hi::buffer-document buffer) (%setf-macptr (%null-ptr) self))))
     1049    self))
    1281050   
    1291051 
     1052(defmethod hemlock-document-buffer (document)
     1053  (let* ((string (send (slot-value document 'textstorage) 'string)))
     1054    (unless (%null-ptr-p string)
     1055      (let* ((cache (hemlock-buffer-string-cache string)))
     1056        (when cache (buffer-cache-buffer cache))))))
    1301057
    1311058(define-objc-method ((:id :data-representation-of-type type)
    1321059                      lisp-editor-document)
    1331060  (declare (ignorable type))
    134   (send (send (slot-value self 'text-view) 'string)
     1061  (let* ((buffer (hemlock-document-buffer self)))
     1062    (when buffer
     1063      (setf (hi::buffer-modified buffer) nil)))
     1064  (send (send (slot-value self 'textstorage) 'string)
    1351065        :data-using-encoding #$NSASCIIStringEncoding
    1361066        :allow-lossy-conversion t))
    1371067
     1068
     1069;;; Shadow the setFileName: method, so that we can keep the buffer
     1070;;; name and pathname in synch with the document.
     1071(define-objc-method ((:void :set-file-name full-path)
     1072                     lisp-editor-document)
     1073  (send-super :set-file-name full-path)
     1074  (let* ((buffer (hemlock-document-buffer self)))
     1075    (when buffer
     1076      (let* ((new-pathname (lisp-string-from-nsstring full-path)))
     1077        (setf (hi::buffer-name buffer) (hi::pathname-to-buffer-name new-pathname))
     1078        (setf (hi::buffer-pathname buffer) new-pathname)))))
     1079 
    1381080(define-objc-method ((:void make-window-controllers) lisp-editor-document)
    1391081  (let* ((controller (make-objc-instance
     
    1931135    (setf (slot-value self 'textstorage) (%null-ptr))
    1941136    (unless (%null-ptr-p textstorage)
    195       (close-hemlock-textstorage textstorage)))
    196   (let* ((info (info-from-document self)))
    197     (when info
    198       (let* ((proc (cocoa-editor-info-listener info)))
    199         (when proc
    200               (setf (cocoa-editor-info-listener info) nil)
    201               (process-kill proc)))
    202       (with-lock-grabbed (*open-editor-documents-lock*)
    203         (setq *open-editor-documents*
    204               (delete info *open-editor-documents*))))))
     1137      (close-hemlock-textstorage textstorage))))
    2051138
    2061139
  • trunk/ccl/examples/cocoa-listener.lisp

    r569 r592  
    2222         (type list *open-editor-documents*))
    2323
    24 #-hemlock
    25 (progn
     24
     25(defloadvar *cocoa-listener-count* 0)
     26
     27
    2628(defun new-listener-process (procname input-fd output-fd)
    2729  (make-mcl-listener-process
     
    3840                                    #$_PC_MAX_INPUT))
    3941   #'(lambda ()
    40        (let* ((info (with-lock-grabbed (*open-editor-documents-lock*)
    41                       (find *current-process* *open-editor-documents*
    42                             :key #'cocoa-editor-info-listener))))
    43          (when info
    44            (setf (cocoa-editor-info-listener info) nil)
    45            (send (cocoa-editor-info-document info)
     42       (let* ((buf (find *current-process* hi:*buffer-list*
     43                         :key #'hi::buffer-process))
     44              (doc (if buf (hi::buffer-document buf))))
     45         (when doc
     46           (setf (hi::buffer-process buf) nil)
     47           (send doc
    4648                 :perform-selector-on-main-thread (@selector "close")
    4749                 :with-object (%null-ptr)
     
    6264    ((filehandle :foreign-type :id)     ;Filehandle for I/O
    6365     (clientfd :foreign-type :int)      ;Client (listener)'s side of pty
    64      (outpos :foreign-type :unsigned)   ;Position in textview buffer
    65      (userta :foreign-type :id)         ;Typing attributes for user input
    66      (systa :foreign-type :id)          ;Typing attributes for system output
    67      (usercolor :foreign-type :id)      ;Text foreground color for user input
    6866     )
    6967  (:metaclass ns:+ns-object)
    7068  )
    7169
    72 (define-objc-method ((:void window-did-load) lisp-listener-window-controller)
    73   (multiple-value-bind (server client) (ignore-errors (open-pty-pair))
    74     (when server
    75       (let* ((fh (make-objc-instance
    76                   'ns-file-handle
    77                   :with-file-descriptor (setup-server-pty server)
    78                   :close-on-dealloc t)))
    79         (setf (slot-value self 'filehandle) fh)
    80         (setf (slot-value self 'clientfd) (setup-client-pty client))
    81         (send (send (@class ns-notification-center) 'default-center)
    82               :add-observer self
    83               :selector (@selector "gotData:")
    84               :name *NSFileHandleReadCompletionNotification*
    85               :object fh)
    86         (send fh 'read-in-background-and-notify)))))
     70(define-objc-method ((:id :init-with-window w)
     71                     lisp-listener-window-controller)
     72  (let* ((self (send-super :init-with-window w)))
     73    (unless (%null-ptr-p self)
     74      (multiple-value-bind (server client) (ignore-errors (open-pty-pair))
     75        (when server
     76          (let* ((fh (make-objc-instance
     77                      'ns-file-handle
     78                      :with-file-descriptor (setup-server-pty server)
     79                      :close-on-dealloc t)))
     80            (setf (slot-value self 'filehandle) fh)
     81            (setf (slot-value self 'clientfd) (setup-client-pty client))
     82            (send (send (@class ns-notification-center) 'default-center)
     83                  :add-observer self
     84                  :selector (@selector "gotData:")
     85                  :name *NSFileHandleReadCompletionNotification*
     86                  :object fh)
     87            (send fh 'read-in-background-and-notify)))))
     88    self))
    8789
    8890(define-objc-method ((:void :got-data notification)
    8991                     lisp-listener-window-controller)
    90   (with-slots (filehandle systa outpos textview) self
     92  (with-slots (filehandle) self
    9193    (let* ((data (send (send notification 'user-info)
    9294                       :object-for-key *NSFileHandleNotificationDataItem*))
    93            (tv textview)
     95           (document (send self 'document))
     96           (data-length (send data 'length))
     97           (buffer (hemlock-document-buffer document))
     98           (string (make-string data-length))
    9499           (fh filehandle))
    95       (unless (%null-ptr-p tv)
    96         (let* ((buffer-text (send tv 'text-storage))
    97                (s (make-objc-instance 'ns-string
    98                                       :with-data data
    99                                       :encoding #$NSASCIIStringEncoding))
    100                (str (make-objc-instance 'ns-attributed-string
    101                                         :with-string s
    102                                         :attributes systa)))
    103           (send buffer-text :append-attributed-string str)
    104 
    105           (let* ((textlen (send buffer-text 'length)))
    106             (send tv :scroll-range-to-visible (ns-make-range textlen 0))
    107             (setq outpos textlen))
    108           (send str 'release)))
    109       (send self 'update-package-name)
     100      (declare (dynamic-extent string))
     101      (%copy-ptr-to-ivector (send data 'bytes) 0 string 0 data-length)
     102      (hi::insert-string (hi::buffer-point buffer) string)
    110103      (send fh 'read-in-background-and-notify))))
    111104             
    112 (define-objc-method ((:void update-package-name)
    113                      lisp-listener-window-controller)
    114   (let* ((info (info-from-controller self))
    115          (proc (if info (cocoa-editor-info-listener info)))
    116          (package (if proc (ignore-errors (symbol-value-in-process
    117                                            '*package*
    118                                            proc))))
    119          (name (if (typep package 'package)
    120                  (shortest-package-name package)
    121                  "")))
    122     (with-cstrs ((name name))
    123       (send self :display-package-name (send (@class ns-string)
    124                                              :string-with-c-string name)))))
    125      
    126 
    127    
     105#|   
    128106;;; The Lisp-Listener-Window-Controller is the textview's "delegate": it
    129107;;; gets consulted before certain actions are performed, and can
     
    142120      (send tv :set-typing-attributes (slot-value self 'userta))
    143121      t)))
    144 
     122|#
    145123
    146124;;; Action methods implemented by the controller (in its role as the
     
    155133                          :allow-lossy-conversion t)))
    156134
    157 (define-objc-method ((:void :insert-newline tv)
    158                      lisp-listener-window-controller)
    159   (with-slots (outpos usercolor) self
    160     (let* ((textbuf (send tv 'text-storage))
    161            (textlen (send textbuf 'length))
    162            (textstring (send tv 'string)))
    163       (slet ((r (send tv 'selected-range)))
    164         (let* ((curpos (pref r :<NSR>ange.location))
    165                (curlen (pref r :<NSR>ange.length)))
    166           (cond ((>= curpos outpos)
    167                  ;; Insert the newline at the end of any selection.
    168                  (incf curpos (pref r :<NSR>ange.length))
    169                  (send tv :set-selected-range (ns-make-range curpos 0))
    170                  (send tv :insert-newline self)
    171                  (incf curpos)
    172                  (incf textlen)
    173                  (when (= curpos textlen)
    174                    (let* ((sendlen (- textlen outpos))
    175                           (sendstring
    176                            (send textstring
    177                                  :substring-with-range (ns-make-range outpos sendlen))))
    178                      (setf (pref r :<NSR>ange.location) 0
    179                            (pref r :<NSR>ange.length) sendlen)
    180                      (multiple-value-bind (ok second-value)
    181                          (balanced-expressions-in-range-forward r sendstring)
    182                        (if ok
    183                          (if second-value
    184                            (progn
    185                              (send self :send-string sendstring)
    186                              (setq outPos textlen)))
    187                          (if second-value
    188                            (#_NSBeep)))))))
    189                 ;; If there's a selection, copy it to the end of the
    190                 ;; buffer, then move to the end of the buffer.
    191                 ((> curlen 0)
    192                  (slet ((endrange (ns-make-range textlen 0)))
    193                    (send tv :set-selected-range endrange)
    194                    (send tv :insert-text
    195                          (send textstring :substring-with-range r))
    196                    (setf (pref endrange :<NSR>ange.location)
    197                          (send textbuf 'length))
    198                    (send tv :scroll-range-to-visible endrange)))
    199                 ;; No selection, insertion point is before outpos (in
    200                 ;; history or in output.  If in history, copy history
    201                 ;; item to end of buffer, otherwise, do nothing.
    202                 (t
    203                  (rlet ((lr :<NSR>ange)
    204                         (fullrange :<NSR>ange :location 0 :length textlen))
    205                    (let* ((attr
    206                            (send textbuf
    207                                  :attribute #@"NSColor"
    208                                  :at-index curpos
    209                                  :longest-effective-range lr
    210                                  :in-range fullrange)))
    211                      (when (send attr :is-equal  usercolor)
    212                        (let* ((history-start (pref lr :<NSR>ange.location))
    213                               (history-len (pref lr :<NSR>ange.length)))
    214                          (when (eql
    215                                 (send textstring
    216                                       :character-at-index
    217                                       (+ history-start (1- history-len)))
    218                                 (char-code #\NewLine))
    219                            (decf (pref lr :<NSR>ange.length)))
    220                          (unless (eql 0 history-len)
    221                            (setf (pref fullrange :<NSR>ange.location)
    222                                  textlen
    223                                  (pref fullrange :<NSR>ange.length)
    224                                  0)
    225                            (send tv :set-selected-range  fullrange)
    226                            (send tv :insert-text
    227                                  (send textstring :substring-with-range lr))
    228                            (setf (pref fullrange :<NSR>ange.location)
    229                                  (send textbuf 'length))
    230                            (send tv :scroll-range-to-visible fullrange)))))))))))))
    231 
    232 ;;; Force a break in the listener process.
    233 (define-objc-method ((:id :interrupt tv) lisp-listener-window-controller)
    234   (declare (ignore tv))
    235   (let* ((info (info-from-controller self))
    236          (proc (if info (cocoa-editor-info-listener info))))
    237     (when proc (force-break-in-listener proc))
    238     self))
    239 
    240 ;;; This exists solely for debugging.
    241 (define-objc-method ((:id :log-attrs tv)  lisp-listener-window-controller)
    242   (slet ((selection (send tv 'selected-range)))
    243     (rlet ((lr :<NSR>ange))
    244       (let* ((textbuf (send tv 'text-storage))
    245              (attr
    246               (send textbuf
    247                     :attributes-at-index (pref selection :<NSR>ange.location)
    248                     :longest-effective-range lr
    249                     :in-range (ns-make-range 0 (send textbuf 'length)))))
    250         (#_NSLog #@"Attr = %@, range = [%d,%d]"
    251                  :address attr
    252                  :unsigned-fullword (pref lr :<NSR>ange.location)
    253                  :unsigned-fullword (pref lr :<NSR>ange.length)))
    254       self)))
    255 
    256 ;;; If we're at the end of the buffer and at the start of a line (either
    257 ;;; at outpos or after a newline), send an EOF (0 bytes of data) to the
    258 ;;; listener.  Otherwise, have the textview do a "deleteForward:"
     135
     136
     137
    259138(define-objc-method ((:id :delete-forward tv)  lisp-listener-window-controller)
    260139  (with-slots (outpos filehandle) self
     
    279158        self))))
    280159
    281 (define-objc-method ((:id :add-modeline tv) lisp-listener-window-controller)
    282   (declare (ignore tv))
    283   self
    284   )
    285 
    286 (define-objc-method ((:id :reparse-modeline tv)
    287                      lisp-listener-window-controller)
    288   (declare (ignore tv))
    289   self
    290   )
     160
     161
     162
    291163
    292164(define-objc-method ((:void dealloc) lisp-listener-window-controller)
     
    297169
    298170
    299 
    300171;;; The LispListenerDocument class.
    301172
     
    305176  (:metaclass ns:+ns-object))
    306177
    307 #-hemlock
    308 (progn
     178
    309179(define-objc-class-method ((:id top-listener) lisp-listener-document)
    310180  (let* ((all-documents (send *NSApp* 'ordered-Documents)))
     
    316186(defun symbol-value-in-top-listener-process (symbol)
    317187  (let* ((listenerdoc (send (@class lisp-listener-document) 'top-listener))
    318          (info (info-from-document listenerdoc))
    319          (process (if info (cocoa-editor-info-listener info))))
     188         (buffer (unless (%null-ptr-p listenerdoc)
     189                   (hemlock-document-buffer listenerdoc)))
     190         (process (if buffer (hi::buffer-process buffer))))
    320191     (if process
    321192       (ignore-errors (symbol-value-in-process symbol process))
     
    327198  nil)
    328199
    329 (define-objc-method ((:void make-window-controllers)  lisp-listener-document)
    330   (let* ((controller (make-objc-instance
    331                       'lisp-listener-window-controller
    332                       :with-window-nib-name (send self 'window-nib-name)
    333                       :owner self)))
    334     (send self :add-window-controller controller)
    335     (send controller 'release)))
    336 
    337      
    338 
    339 
    340 
    341 (defloadvar *cocoa-listener-count* 17)
    342 
    343 (define-objc-method ((:void :window-controller-did-load-nib acontroller)
     200
     201(define-objc-method ((:id init)
    344202                     lisp-listener-document)
    345   (send-super :window-controller-did-load-nib acontroller)
    346   ;; We'll use attribute-change information to distinguish user
    347   ;; input from system output.  Be fascist about letting the
    348   ;; user change anything.
    349   (with-slots ((textview text-view) packagename echoarea filedata) self
    350     (send textview :set-rich-text nil)
    351     (send textview :set-uses-font-panel nil)
    352     (let* ((listener-name (if (eql 1 (incf *cocoa-listener-count*))
     203  (let* ((doc (send-super 'init)))
     204    (unless (%null-ptr-p doc)
     205      (let* ((listener-name (if (eql 1 (incf *cocoa-listener-count*))
    353206                            "Listener"
    354207                            (format nil
    355208                                    "Listener-~d" *cocoa-listener-count*)))
    356            (info (info-from-document self)))
    357       (setf (cocoa-editor-info-listener info)
    358             (let* ((tty (slot-value acontroller 'clientfd)))
    359               (new-listener-process listener-name tty tty)))
    360       (send self :set-file-name  (%make-nsstring listener-name)))
    361     (setf (slot-value acontroller 'textview) textview
    362           (slot-value acontroller 'echoarea) echoarea
    363           (slot-value acontroller 'packagename) packagename)
    364     (let* ((userta (send (send textview 'typing-attributes) 'retain))
    365            (systa (create-text-attributes :color (send (@class ns-color)
    366                                                        'blue-color))))
    367       (setf (slot-value acontroller 'userta)
    368             userta
    369             (slot-value acontroller 'usercolor)
    370             (send userta :value-for-key #@"NSColor")
    371             (slot-value acontroller 'systa)
    372             systa))
    373     (send textview :set-delegate  acontroller)
    374     (unless (%null-ptr-p filedata)
    375       (send textview
    376             :replace-characters-in-range (ns-make-range 0 0)
    377             :with-rtfd filedata))))
     209             (buffer (hemlock-document-buffer doc)))
     210        (send doc :set-file-name  (%make-nsstring listener-name))
     211        (setf (hi::buffer-pathname buffer) nil
     212              (hi::buffer-minor-mode buffer "Listener") t
     213              (hi::buffer-name buffer) listener-name)))
     214    doc))
     215
     216(define-objc-method ((:void make-window-controllers) lisp-listener-document)
     217  (let* ((controller (make-objc-instance
     218                      'lisp-listener-window-controller
     219                      :with-window (%hemlock-frame-for-textstorage
     220                                    (slot-value self 'textstorage) nil nil)))
     221         (listener-name (hi::buffer-name (hemlock-document-buffer self))))
     222    (send self :add-window-controller controller)
     223    (send controller 'release)
     224    (setf (hi::buffer-process (hemlock-document-buffer self))
     225          (let* ((tty (slot-value controller 'clientfd)))
     226            (new-listener-process listener-name tty tty)))
     227    controller))
    378228
    379229;;; This is almost completely wrong: we need to ensure that the form
    380230;;; is read in the correct package, etc.
     231#|
    381232(defun send-to-top-listener (sender-info nsstring &optional (append-newline t))
    382233  (declare (ignorable sender-info))
     
    391242"
    392243          ))))))
    393 
    394 
    395 ); #-hemlock
     244|#
     245
     246
     247
Note: See TracChangeset for help on using the changeset viewer.