Update FSF's address.
[bpt/emacs.git] / lisp / emulation / pc-select.el
CommitLineData
215e89e5
RS
1;;; pc-select.el --- emulate mark, cut, copy and paste from motif
2;;; (or MAC GUI) or MS-windoze (bah)) look-and-feel
3;;; including key bindings
4
5;; Copyright (C) 1995 Free Software Foundation, Inc.
6
7;; Author: Michael Staats <michael@thp.Uni-Duisburg.DE>
8;; Created: 26 Sep 1995
9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software; you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation; either version 2, or (at your option)
15;; any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
b578f267
EN
23;; along with GNU Emacs; see the file COPYING. If not, write to the
24;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25;; Boston, MA 02111-1307, USA.
215e89e5
RS
26
27;;; Commentary:
b578f267 28
215e89e5
RS
29;; This package emulates the mark, copy, cut and paste look-and-feel of motif
30;; programs (which is the same as the MAC gui and (sorry for that) MS-Windows).
31;; It modifies the keybindings of the cursor keys and the next, prior,
32;; home and end keys. They will modify mark-active.
33;; You can still get the old behaviour of cursor moving with the
34;; control sequences C-f, C-b, etc.
35;; This package uses transient-mark-mode and
36;; delete-selection-mode.
37;;
38;; In addition to that all key-bindings from the pc-mode are
39;; done here too (as suggested by RMS).
40;;
41;; As I found out after I finished the first version, s-region.el tries
42;; to do the same.... But my code is a little more complete and using
43;; delete-selection-mode is very important for the look-and-feel.
44;; Pete Forman <pete.forman@airgun.wg.waii.com> provided some motif
45;; compliant keybindings which I added. I had to modify them a little
46;; to add the -mark and -nomark functionality of cursor moving.
47;;
48;; Credits:
49;; Many thanks to all who made comments.
50;; Thanks to RMS and Ralf Muschall <prm@rz.uni-jena.de> for criticism.
51;; Kevin Cutts <cutts@ukraine.corp.mot.com> added the beginning-of-buffer
52;; and end-of-buffer functions which I modified a little.
53;; David Biesack <sasdjb@unx.sas.com> suggested some more cleanup.
54;; Thanks to Pete Forman <pete.forman@airgun.wg.waii.com>
55;; for additional motif keybindings.
56;;
57;;
58;; Ok, some details about the idea of pc-selection-mode:
59;;
60;; o The standard keys for moving around (right, left, up, down, home, end,
61;; prior, next, called "move-keys" from now on) will always de-activate
62;; the mark.
63;; o If you press "Shift" together with the "move-keys", the region
64;; you pass along is activated
65;; o You have the copy, cut and paste functions (as in many other programs)
66;; which will operate on the active region
67;; It was not possible to bind them to C-v, C-x and C-c for obvious
68;; emacs reasons.
69;; They will be bound according to the "old" behaviour to S-delete (cut),
70;; S-insert (paste) and C-insert (copy). These keys do the same in many
71;; other programs.
215e89e5 72
b578f267 73;;; Code:
215e89e5
RS
74
75;;;;
76;; misc
77;;;;
78
79(provide 'pc-select)
80
81(defun copy-region-as-kill-nomark (beg end)
82 "Save the region as if killed; but don't kill it; deactivate mark.
83If `interprogram-cut-function' is non-nil, also save the text for a window
84system cut and paste.\n
85Deactivating mark is to avoid confusion with delete-selection-mode
86and transient-mark-mode."
87 (interactive "r")
88 (copy-region-as-kill beg end)
89 (setq mark-active nil)
90 (message "Region saved"))
91
92;;;;
93;; non-interactive
94;;;;
95(defun ensure-mark()
96 ;; make sure mark is active
97 ;; test if it is active, if it isn't, set it and activate it
98 (and (not mark-active) (set-mark-command nil)))
99
100;;;;;;;;;;;;;;;;;;;;;;;;;;;
101;;;;; forward and mark
102;;;;;;;;;;;;;;;;;;;;;;;;;;;
103
104(defun forward-char-mark (&optional arg)
105 "Ensure mark is active; move point right ARG characters (left if ARG negative).
106On reaching end of buffer, stop and signal error."
107 (interactive "p")
108 (ensure-mark)
109 (forward-char arg))
110
111(defun forward-word-mark (&optional arg)
112 "Ensure mark is active; move point right ARG words (backward if ARG is negative).
113Normally returns t.
114If an edge of the buffer is reached, point is left there
115and nil is returned."
116 (interactive "p")
117 (ensure-mark)
118 (forward-word arg))
119
120(defun forward-paragraph-mark (&optional arg)
121 "Ensure mark is active; move forward to end of paragraph.
122With arg N, do it N times; negative arg -N means move backward N paragraphs.\n
123A line which `paragraph-start' matches either separates paragraphs
124(if `paragraph-separate' matches it also) or is the first line of a paragraph.
125A paragraph end is the beginning of a line which is not part of the paragraph
126to which the end of the previous line belongs, or the end of the buffer."
127 (interactive "p")
128 (ensure-mark)
129 (forward-paragraph arg))
130
131(defun next-line-mark (&optional arg)
132 "Ensure mark is active; move cursor vertically down ARG lines.
133If there is no character in the target line exactly under the current column,
134the cursor is positioned after the character in that line which spans this
135column, or at the end of the line if it is not long enough.
136If there is no line in the buffer after this one, behavior depends on the
137value of `next-line-add-newlines'. If non-nil, it inserts a newline character
138to create a line, and moves the cursor to that line. Otherwise it moves the
139cursor to the end of the buffer \(if already at the end of the buffer, an error
140is signaled).\n
141The command C-x C-n can be used to create
142a semipermanent goal column to which this command always moves.
143Then it does not try to move vertically. This goal column is stored
144in `goal-column', which is nil when there is none."
145 (interactive "p")
146 (ensure-mark)
147 (next-line arg))
148
149(defun end-of-line-mark (&optional arg)
150 "Ensure mark is active; move point to end of current line.
151With argument ARG not nil or 1, move forward ARG - 1 lines first.
152If scan reaches end of buffer, stop there without error."
153 (interactive "p")
154 (ensure-mark)
155 (end-of-line arg))
156
157(defun scroll-down-mark (&optional arg)
158 "Ensure mark is active; scroll down ARG lines; or near full screen if no ARG.
159A near full screen is `next-screen-context-lines' less than a full screen.
160Negative ARG means scroll upward.
161When calling from a program, supply a number as argument or nil."
162 (interactive "P")
163 (ensure-mark)
164 (scroll-down arg))
165
166(defun end-of-buffer-mark (&optional arg)
167 "Ensure mark is active; move point to the end of the buffer.
168With arg N, put point N/10 of the way from the end.\n
169If the buffer is narrowed, this command uses the beginning and size
170of the accessible part of the buffer.\n
171Don't use this command in Lisp programs!
172\(goto-char \(point-max)) is faster and avoids clobbering the mark."
173 (interactive "P")
174 (ensure-mark)
175 (let ((size (- (point-max) (point-min))))
176 (goto-char (if arg
177 (- (point-max)
178 (if (> size 10000)
179 ;; Avoid overflow for large buffer sizes!
180 (* (prefix-numeric-value arg)
181 (/ size 10))
182 (/ (* size (prefix-numeric-value arg)) 10)))
183 (point-max))))
184 ;; If we went to a place in the middle of the buffer,
185 ;; adjust it to the beginning of a line.
186 (if arg (forward-line 1)
187 ;; If the end of the buffer is not already on the screen,
188 ;; then scroll specially to put it near, but not at, the bottom.
189 (if (let ((old-point (point)))
190 (save-excursion
191 (goto-char (window-start))
192 (vertical-motion (window-height))
193 (< (point) old-point)))
194 (progn
195 (overlay-recenter (point))
196 (recenter -3)))))
197
198;;;;;;;;;
199;;;;; no mark
200;;;;;;;;;
201
202(defun forward-char-nomark (&optional arg)
203 "Deactivate mark; move point right ARG characters \(left if ARG negative).
204On reaching end of buffer, stop and signal error."
205 (interactive "p")
206 (setq mark-active nil)
207 (forward-char arg))
208
209(defun forward-word-nomark (&optional arg)
210 "Deactivate mark; move point right ARG words \(backward if ARG is negative).
211Normally returns t.
212If an edge of the buffer is reached, point is left there
213and nil is returned."
214 (interactive "p")
215 (setq mark-active nil)
216 (forward-word arg))
217
218(defun forward-paragraph-nomark (&optional arg)
219 "Deactivate mark; move forward to end of paragraph.
220With arg N, do it N times; negative arg -N means move backward N paragraphs.\n
221A line which `paragraph-start' matches either separates paragraphs
222(if `paragraph-separate' matches it also) or is the first line of a paragraph.
223A paragraph end is the beginning of a line which is not part of the paragraph
224to which the end of the previous line belongs, or the end of the buffer."
225 (interactive "p")
226 (setq mark-active nil)
227 (forward-paragraph arg))
228
229(defun next-line-nomark (&optional arg)
230 "Deactivate mark; move cursor vertically down ARG lines.
231If there is no character in the target line exactly under the current column,
232the cursor is positioned after the character in that line which spans this
233column, or at the end of the line if it is not long enough.
234If there is no line in the buffer after this one, behavior depends on the
235value of `next-line-add-newlines'. If non-nil, it inserts a newline character
236to create a line, and moves the cursor to that line. Otherwise it moves the
237cursor to the end of the buffer (if already at the end of the buffer, an error
238is signaled).\n
239The command C-x C-n can be used to create
240a semipermanent goal column to which this command always moves.
241Then it does not try to move vertically. This goal column is stored
242in `goal-column', which is nil when there is none."
243 (interactive "p")
244 (setq mark-active nil)
245 (next-line arg))
246
247(defun end-of-line-nomark (&optional arg)
248 "Deactivate mark; move point to end of current line.
249With argument ARG not nil or 1, move forward ARG - 1 lines first.
250If scan reaches end of buffer, stop there without error."
251 (interactive "p")
252 (setq mark-active nil)
253 (end-of-line arg))
254
255(defun scroll-down-nomark (&optional arg)
256 "Deactivate mark; scroll down ARG lines; or near full screen if no ARG.
257A near full screen is `next-screen-context-lines' less than a full screen.
258Negative ARG means scroll upward.
259When calling from a program, supply a number as argument or nil."
260 (interactive "P")
261 (setq mark-active nil)
262 (scroll-down arg))
263
264(defun end-of-buffer-nomark (&optional arg)
265 "Deactivate mark; move point to the end of the buffer.
266With arg N, put point N/10 of the way from the end.\n
267If the buffer is narrowed, this command uses the beginning and size
268of the accessible part of the buffer.\n
269Don't use this command in Lisp programs!
270(goto-char (point-max)) is faster and avoids clobbering the mark."
271 (interactive "P")
272 (setq mark-active nil)
273 (let ((size (- (point-max) (point-min))))
274 (goto-char (if arg
275 (- (point-max)
276 (if (> size 10000)
277 ;; Avoid overflow for large buffer sizes!
278 (* (prefix-numeric-value arg)
279 (/ size 10))
280 (/ (* size (prefix-numeric-value arg)) 10)))
281 (point-max))))
282 ;; If we went to a place in the middle of the buffer,
283 ;; adjust it to the beginning of a line.
284 (if arg (forward-line 1)
285 ;; If the end of the buffer is not already on the screen,
286 ;; then scroll specially to put it near, but not at, the bottom.
287 (if (let ((old-point (point)))
288 (save-excursion
289 (goto-char (window-start))
290 (vertical-motion (window-height))
291 (< (point) old-point)))
292 (progn
293 (overlay-recenter (point))
294 (recenter -3)))))
295
296
297;;;;;;;;;;;;;;;;;;;;
298;;;;;; backwards and mark
299;;;;;;;;;;;;;;;;;;;;
300
301(defun backward-char-mark (&optional arg)
302"Ensure mark is active; move point left ARG characters (right if ARG negative).
303On attempt to pass beginning or end of buffer, stop and signal error."
304 (interactive "p")
305 (ensure-mark)
306 (backward-char arg))
307
308(defun backward-word-mark (&optional arg)
309 "Ensure mark is active; move backward until encountering the end of a word.
310With argument, do this that many times."
311 (interactive "p")
312 (ensure-mark)
313 (backward-word arg))
314
315(defun backward-paragraph-mark (&optional arg)
316 "Ensure mark is active; move backward to start of paragraph.
317With arg N, do it N times; negative arg -N means move forward N paragraphs.\n
318A paragraph start is the beginning of a line which is a
319`first-line-of-paragraph' or which is ordinary text and follows a
320paragraph-separating line; except: if the first real line of a
321paragraph is preceded by a blank line, the paragraph starts at that
322blank line.\n
323See `forward-paragraph' for more information."
324 (interactive "p")
325 (ensure-mark)
326 (backward-paragraph arg))
327
328(defun previous-line-mark (&optional arg)
329 "Ensure mark is active; move cursor vertically up ARG lines.
330If there is no character in the target line exactly over the current column,
331the cursor is positioned after the character in that line which spans this
332column, or at the end of the line if it is not long enough.\n
333The command C-x C-n can be used to create
334a semipermanent goal column to which this command always moves.
335Then it does not try to move vertically.\n
336If you are thinking of using this in a Lisp program, consider using
337`forward-line' with a negative argument instead. It is usually easier
338to use and more reliable (no dependence on goal column, etc.)."
339 (interactive "p")
340 (ensure-mark)
341 (previous-line arg))
342
343(defun beginning-of-line-mark (&optional arg)
344 "Ensure mark is active; move point to beginning of current line.
345With argument ARG not nil or 1, move forward ARG - 1 lines first.
346If scan reaches end of buffer, stop there without error."
347 (interactive "p")
348 (ensure-mark)
349 (beginning-of-line arg))
350
351
352(defun scroll-up-mark (&optional arg)
353"Ensure mark is active; scroll upward ARG lines; or near full screen if no ARG.
354A near full screen is `next-screen-context-lines' less than a full screen.
355Negative ARG means scroll downward.
356When calling from a program, supply a number as argument or nil."
357 (interactive "P")
358 (ensure-mark)
359 (scroll-up arg))
360
361(defun beginning-of-buffer-mark (&optional arg)
362 "Ensure mark is active; move point to the beginning of the buffer.
363With arg N, put point N/10 of the way from the beginning.\n
364If the buffer is narrowed, this command uses the beginning and size
365of the accessible part of the buffer.\n
366Don't use this command in Lisp programs!
367\(goto-char (p\oint-min)) is faster and avoids clobbering the mark."
368 (interactive "P")
369 (ensure-mark)
370 (let ((size (- (point-max) (point-min))))
371 (goto-char (if arg
372 (+ (point-min)
373 (if (> size 10000)
374 ;; Avoid overflow for large buffer sizes!
375 (* (prefix-numeric-value arg)
376 (/ size 10))
377 (/ (+ 10 (* size (prefix-numeric-value arg))) 10)))
378 (point-min))))
379 (if arg (forward-line 1)))
380
381;;;;;;;;
382;;; no mark
383;;;;;;;;
384
385(defun backward-char-nomark (&optional arg)
386 "Deactivate mark; move point left ARG characters (right if ARG negative).
387On attempt to pass beginning or end of buffer, stop and signal error."
388 (interactive "p")
389 (setq mark-active nil)
390 (backward-char arg))
391
392(defun backward-word-nomark (&optional arg)
393 "Deactivate mark; move backward until encountering the end of a word.
394With argument, do this that many times."
395 (interactive "p")
396 (setq mark-active nil)
397 (backward-word arg))
398
399(defun backward-paragraph-nomark (&optional arg)
400 "Deactivate mark; move backward to start of paragraph.
401With arg N, do it N times; negative arg -N means move forward N paragraphs.\n
402A paragraph start is the beginning of a line which is a
403`first-line-of-paragraph' or which is ordinary text and follows a
404paragraph-separating line; except: if the first real line of a
405paragraph is preceded by a blank line, the paragraph starts at that
406blank line.\n
407See `forward-paragraph' for more information."
408 (interactive "p")
409 (setq mark-active nil)
410 (backward-paragraph arg))
411
412(defun previous-line-nomark (&optional arg)
413 "Deactivate mark; move cursor vertically up ARG lines.
414If there is no character in the target line exactly over the current column,
415the cursor is positioned after the character in that line which spans this
416column, or at the end of the line if it is not long enough.\n
417The command C-x C-n can be used to create
418a semipermanent goal column to which this command always moves.
419Then it does not try to move vertically."
420 (interactive "p")
421 (setq mark-active nil)
422 (previous-line arg))
423
424(defun beginning-of-line-nomark (&optional arg)
425 "Deactivate mark; move point to beginning of current line.
426With argument ARG not nil or 1, move forward ARG - 1 lines first.
427If scan reaches end of buffer, stop there without error."
428 (interactive "p")
429 (setq mark-active nil)
430 (beginning-of-line arg))
431
432(defun scroll-up-nomark (&optional arg)
433 "Deactivate mark; scroll upward ARG lines; or near full screen if no ARG.
434A near full screen is `next-screen-context-lines' less than a full screen.
435Negative ARG means scroll downward.
436When calling from a program, supply a number as argument or nil."
437 (interactive "P")
438 (setq mark-active nil)
439 (scroll-up arg))
440
441(defun beginning-of-buffer-nomark (&optional arg)
442 "Deactivate mark; move point to the beginning of the buffer.
443With arg N, put point N/10 of the way from the beginning.\n
444If the buffer is narrowed, this command uses the beginning and size
445of the accessible part of the buffer.\n
446Don't use this command in Lisp programs!
447(goto-char (point-min)) is faster and avoids clobbering the mark."
448 (interactive "P")
449 (setq mark-active nil)
450 (let ((size (- (point-max) (point-min))))
451 (goto-char (if arg
452 (+ (point-min)
453 (if (> size 10000)
454 ;; Avoid overflow for large buffer sizes!
455 (* (prefix-numeric-value arg)
456 (/ size 10))
457 (/ (+ 10 (* size (prefix-numeric-value arg))) 10)))
458 (point-min))))
459 (if arg (forward-line 1)))
460
3eeb7b9f 461;;;###autoload
215e89e5
RS
462(defun pc-selection-mode ()
463 "Change mark behaviour to emulate motif, MAC or MS-Windows cut and paste style.\n
464This mode will switch on delete-selection-mode and
465transient-mark-mode.\n
466The cursor keys (and others) are bound to new functions
467which will modify the status of the mark. It will be
468possible to select regions with shift-cursorkeys. All this
469tries to emulate the look-and-feel of GUIs like motif,
470the MAC GUI or MS-Windows (sorry for the last one)."
471 (interactive)
472 ;;
473 ;; keybindings
474 ;;
475
476 ;; This is to avoid confusion with the delete-selection-mode
477 ;; On simple displays you can't see that a region is active and
478 ;; will be deleted on the next keypress. IMHO especially for
479 ;; copy-region-as-kill this is confusing
480 (define-key global-map "\M-w" 'copy-region-as-kill-nomark)
481
482
a7acbbe4 483 ;; The following keybindings are for standard ISO keyboards
215e89e5
RS
484 ;; as they are used with IBM compatible PCs, IBM RS/6000,
485 ;; MACs, many X-Stations and probably more
486 (define-key global-map [S-right] 'forward-char-mark)
487 (define-key global-map [right] 'forward-char-nomark)
488 (define-key global-map [C-S-right] 'forward-word-mark)
489 (define-key global-map [C-right] 'forward-word-nomark)
490
491 (define-key global-map [S-down] 'next-line-mark)
492 (define-key global-map [down] 'next-line-nomark)
493
494 (define-key global-map [S-end] 'end-of-line-mark)
495 (define-key global-map [end] 'end-of-line-nomark)
496 (global-set-key [S-C-end] 'end-of-buffer-mark)
497 (global-set-key [C-end] 'end-of-buffer-nomark)
498
499 (define-key global-map [S-next] 'scroll-up-mark)
500 (define-key global-map [next] 'scroll-up-nomark)
501
502 (define-key global-map [S-left] 'backward-char-mark)
503 (define-key global-map [left] 'backward-char-nomark)
504 (define-key global-map [C-S-left] 'backward-word-mark)
505 (define-key global-map [C-left] 'backward-word-nomark)
506
507 (define-key global-map [S-up] 'previous-line-mark)
508 (define-key global-map [up] 'previous-line-nomark)
509
510 (define-key global-map [S-home] 'beginning-of-line-mark)
511 (define-key global-map [home] 'beginning-of-line-nomark)
512 (global-set-key [S-C-home] 'beginning-of-buffer-mark)
513 (global-set-key [C-home] 'beginning-of-buffer-nomark)
514
515 (define-key global-map [S-prior] 'scroll-down-mark)
516 (define-key global-map [prior] 'scroll-down-nomark)
517
518 (define-key global-map [S-insert] 'yank)
519 (define-key global-map [C-insert] 'copy-region-as-kill)
520 (define-key global-map [S-delete] 'kill-region)
521
a7acbbe4 522 ;; The following bindings are useful on Sun Type 3 keyboards
215e89e5
RS
523 ;; They implement the Get-Delete-Put (copy-cut-paste)
524 ;; functions from sunview on the L6, L8 and L10 keys
525 (define-key global-map [f16] 'yank)
526 (define-key global-map [f18] 'copy-region-as-kill)
527 (define-key global-map [f20] 'kill-region)
528
529 ;; The following bindings are from Pete Forman.
530 ;; I modified them a little to work together with the
531 ;; mark functionality I added.
532
533 (global-set-key [f1] 'help) ; KHelp F1
534 (global-set-key [f6] 'other-window) ; KNextPane F6
535 (global-set-key [delete] 'delete-char) ; KDelete Del
536 (global-set-key [C-delete] 'kill-line) ; KEraseEndLine cDel
537 (global-set-key [M-backspace] 'undo) ; KUndo aBS
215e89e5
RS
538 (global-set-key [C-down] 'forward-paragraph-nomark) ; KNextPara cDn
539 (global-set-key [C-up] 'backward-paragraph-nomark) ; KPrevPara cUp
540 (global-set-key [S-C-down] 'forward-paragraph-mark)
541 (global-set-key [S-C-up] 'backward-paragraph-mark)
542
543 ;; The following bindings are taken from pc-mode.el
544 ;; as suggested by RMS.
545 ;; I only used the ones that are not covered above.
546 (define-key function-key-map [M-delete] [?\M-d])
547 (global-set-key [C-M-delete] 'kill-sexp)
548 (global-set-key [C-backspace] 'backward-kill-word)
549 (global-set-key [C-escape] 'list-buffers)
550
551 ;;
552 ;; setup
553 ;;
554 (setq transient-mark-mode t)
555 (setq mark-even-if-inactive t)
556 (delete-selection-mode 1))
557
558;;; pc-select.el ends here