🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: gnu.epoch.misc
Article #1794

Re: mouse dragging/selection/editing

#1794
From: Brian L. Kahn
Date: Tue, 17 Sep 1991 16:17
230 lines
8220 bytes
In an article bp@mips.com (Boris Putanec) writes:
   I want things like double-click on word, triple-click to get the
   sentence, with the selection extending at the current selection unit.
   Use the selected text as a large insertion point, so one can select
   something, and then type the replacement text. Etc.



Wasn't exactly a flood of replies to this request, so here is a bit of
code that seems to work for me.  May have some bugs, but should at
least get people started.  I bind these functions to my left and right
mouse buttons, works pretty much like xterm selection.  I particularly
like selecting s-expressions in lisp mode!


--- cut here ---
;;; mouse-select.el
;;; revised to support word and sentence selection, triple click
;;; blk@mitre.org - Wed Sep 11 09:39:15 1991
;;; revised to support timed double clicks
;;; blk@mitre.org - Tue Jan 22 15:35:05 1991

(require 'button)
(require 'mouse)
(provide 'multi-click)


;;; This code installs a mouse-button handler which checks for multi-clicks.
;;; Each up-click is time-stamped, and a subsequent down-click within the
;;; delay interval increments the mouse::clicks counter.
;;; Functions bound to the mouse must check the click counter if they care.
;;; Functions recognizing multi-clicks must be progressive, since the
;;; function will be dispatched first as single click, then double, etc.
;;; The code could be modified to wait for the delay period to see if
;;; any more clicks are coming, but this burns CPU and is very nasty.
;;;
;;; This file includes functions to replace the selection mechanism,
;;; allowing word and line selection.  (mouse::align) extends the region
;;; along object boundaries, char/word/sentence/line/sexp.  There is also 
;;; a function which selects the boundary to use by checking the mode name:
;;;  char/word/sentence for text, char/word/sexp for lisp, etc.
;;; Modify (mouse::boundary) if you don't like this arrangement.
;;;
;;; Copyright MITRE corporation and Brian L. Kahn, Thu Sep 12 13:43:39 1991
;;; Permission granted for any and all use, provided proper citation of 
;;; author and corporation is included in source and/or object.


;;; initialization of mouse selection
;;; =================================

(defvar mouse::style (make-style) 
  "Style used by mouse::attribute")
(defvar mouse::attribute (reserve-attribute) 
  "Attribute for mouse::selection")
(defvar mouse::selection (make-button) 
  "Mouse selection button")

(set-style-foreground mouse::style (background))
(set-style-background mouse::style (foreground))
(set-style-underline mouse::style (background))
(set-attribute-style mouse::attribute mouse::style)
(set-button-attribute mouse::selection mouse::attribute)



;;; mouse handler with multiple clicks
;;; ==================================

(defconst mouse::interval 200
  "The number of milliseconds allowed between multi-clicks
   - interval is clocked between a down-click and previous up-click")
(defvar mouse::time-stamp (the-time)
  "The millisecond time of last upclick")
(defvar mouse::clicks 1
  "The number of mouse button clicks, normally one")

(defvar mouse::old-handler (car (resume-event 'button)) 
  "Previous mouse handler")


(defun mouse::multi-click-handler (type value scr)
  "Calls the old handler after checking for multiple clicks."
  ;; value is a 5-tuple of form (PRESS/RELEASE X Y BUTTON MOD-STATE)
  (if (car value) 
      ;; down-click - do timing checks
      (let ((elapsed (- (the-time) mouse::time-stamp)))
	(if (< elapsed mouse::interval) ; multi-click!
	    (setq mouse::clicks (1+ mouse::clicks))
	  (setq mouse::clicks 1))
	)
    ;; up-click - update time stamp
    (setq mouse::time-stamp (the-time))
    )
  ;; handle as a normal mouse button event
  (funcall mouse::old-handler type value scr)
  )

(push-event 'button 'mouse::multi-click-handler)
(setq epoch::buttons-modify-buffer nil)




;;; selection
;;; =========

(defun mouse::start-select (&optional args)
  "Move mouse and cancel selection??"
  ;; args means mouse press
  (if args (mouse::set-point args))
  (let ((boundary (mouse::boundary mouse::clicks)))
    ;; store selection boundary in data field, for extending selection
    (set-button-data mouse::selection boundary)
    (if (null boundary)
	;; unselect
	(move-button mouse::selection (point) (point) (current-buffer))
      ;; do word/sentence expansion
      (mouse::align (point) (point) boundary)
      (move-button mouse::selection (mark) (point) (current-buffer))
      )
    (redisplay-screen)
    ))



(defun mouse::extend-select (&optional args)
  "Reset near end of selection to mouse pointer or point; adjust region."
  ;; set point if mouse button clicked
  (if args (mouse::set-point args))
  ;; start selection if none in this buffer
  (if (not (eq (current-buffer) (button-buffer mouse::selection)))
      (mouse::start-select)
    (let* ((start (button-start mouse::selection))
	   (end (button-end mouse::selection))
	   (center (/ (+ start end) 2))
	   )
      ;; extending which end?
      (if (> (point) center)
	  (set-mark start)
	(set-mark end))
      ;; check for word/line/etc expansion
      (let ((boundary (button-data mouse::selection)))
	(if boundary (mouse::align (mark) (point) boundary)))
      ;; reposition the selection
      (move-button mouse::selection (mark) (point) (current-buffer))
      (redisplay-screen)
      )))

    

(defun mouse::end-select (args)
  "Selection to cut buffer."
  (let ((text (button-text mouse::selection)))
    (if (not (equal "" text))
	(epoch::store-cut-buffer text))
    ))



(defun mouse::align (from to boundary)
  "Align the region FROM - TO along BOUNDARY; 
   word, line, sent, sexp;
   sets point and mark; returns new region as an apair."
  ;; ensure order of from, to
  (condition-case nil
      (progn
	;; do both ends
	(goto-char (min from to))
	(cond
	 ;; nil and 'char do nothing
	 ((null boundary))
	 ((eq 'char boundary))
	 ((eq 'word boundary) 
	  (re-search-backward "\\<"))
	 ((eq 'line boundary)
	  (beginning-of-line))
	 ((eq 'sent boundary) 
	  (re-search-backward sentence-end)
	  (goto-char (match-end 0)))
	 ((eq 'sexp boundary) 
	  (forward-char 1)
	  (up-list -1))
	 )
	(push-mark)

	;; do both ends
	(goto-char (max from to))
	(cond
	 ;; nil and 'char do nothing
	 ((null boundary))
	 ((eq 'char boundary))
	 ((eq 'word boundary) 
	  (re-search-forward "\\>"))
	 ((eq 'line boundary)
	  (end-of-line))
	 ((eq 'sent boundary) 
	  (re-search-forward sentence-end)
	  (goto-char (1+ (match-beginning 0))))
	 ((eq 'sexp boundary) 
	  (forward-char -1)
	  (up-list 1))
	 )
	(cons (mark) (point))
	)
    ;; if we can't match a search pattern, use 'line
    (error (mouse::align from to 'line)) ; this should always work
    ))



(defun mouse::boundary (&optional level)
  "Returns a suitable boundary, depending upon mode."
  ;; Get the first word from the mode name
  (let ((mode (if (string-match "\w*\\>" mode-name)
		  (substring mode-name 0 (match-end 0)))
	      ))
    (cond
     ;; level 2 is word, level >2 is mode dependent
     ((eq level 1) nil) ; defaults to 'char
     ((eq level 2) 'word)
     ((equal mode "Text") 'sent)
     ((equal mode "Lisp") 'sexp)
     ((equal mode "Tex") 'sent)
     (t 'line)
     )))

--
B<   Brian Kahn   blk@security.mitre.org   "may the farce be with you"
Technical Details
Message-ID: <BLK.91Sep17181750@vanity.mitre.org>
Path: arc1-ca.pugleaf.net!gmdzi!unido!mcsun!uunet!stanford.edu!agate!linus!linus!news!mbunix!blk
References: <BP.91Sep10221746@proton.mips.com>

Show Full Headers
Complete Article Headers
X-Google-Language: ENGLISH,ASCII-7-bit
X-Google-Thread: fc7ee,80751862f3eaecfd
X-Google-Attributes: gidfc7ee,public
X-Google-ArrivalTime: 1991-09-18 04:13:02 PST
Path: arc1-ca.pugleaf.net!gmdzi!unido!mcsun!uunet!stanford.edu!agate!linus!linus!news!mbunix!blk
From: blk@mitre.org (Brian L. Kahn)
Newsgroups: gnu.epoch.misc
Subject: Re: mouse dragging/selection/editing
Message-ID: <BLK.91Sep17181750@vanity.mitre.org>
Date: 17 Sep 91 16:17:50 GMT
References: <BP.91Sep10221746@proton.mips.com>
Sender: news@linus.mitre.org (News Service)
Organization: The MITRE Corp. Bedford, MA
Lines: 230
In-Reply-To: bp@mips.com's message of 11 Sep 91 04:17:46 GMT
Nntp-Posting-Host: vanity.mitre.org