Re: Prefix arguments



 la 02/01/2011 06:27 AM Teika Kazura skribis:
On Thu, 27 Jan 2011 22:02:36 +0100, Daniel Pfeiffer wrote:
Oh, then I must have a patched version of viewports.  I can shift them
by any number of pixels.  I will explore, if there is anything I can
send you, but it'll be a week.
Thanks. Don't mind when it'll be. We core developers are slow anyway. :)
(It's really encouraging to see a veteran like you is contributing
again.)

Well, I'm just surfacing again, and there's still sooo much to do around the new appartment. Besides I don't know GIT, so it would take me more time to dig out 1.5.3 (at least I think that's what I was based on...) and transform this into a patch. I suppose the modification should be trivial and easily repeatable on the current source by anybody after a quick glance.

Next / previous workspace would be a good candidate, given that
mapping/unmapping some windows (in WSs I don't want to go to) is a
waste of time.
Sounds good.

An alternative for grow/pack/shrink/yank is to prepare a dedicated
mode, so that repeating grow and switching among GPSY is easy enough.

Not sure what you're trying to tell me here, but never mind. ;-)

coralament / best Grötens / liebe Grüße / best regards / elkorajn salutojn
Daniel Pfeiffer

--
lerne / learn / apprends / lär dig / ucz się    Esperanto:
                    http://lernu.net  /  http://ikurso.net

;; workspace.jl -- similar to virtual desktops
;; $Id: workspace.jl,v 1.162 2003/04/03 02:50:12 jsh Exp $

;; Copyright (C) 1999 John Harper <john dcs warwick ac uk>

;; This file is part of sawfish.

;; sawfish is free software; you can redistribute it and/or modify it
;; under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; sawfish is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with sawfish; see the file COPYING.  If not, write to
;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

;; Commentary:

;; Sawmill's workspace are organised as a 1-dimensional continuum, from
;; -infinity to +infinity. Normally the user only sees a small part of
;; this continuum, from the first non-empty workspace to the last
;; non-empty workspace

;; So that we never run out of workspace ids the non-empty portion is
;; intermittently normalised to start with index zero

;; Inserting and deleting workspaces just involves shuffling the
;; workspace index of each window

;; The intention is that whatever structure the user wants to see (e.g.
;; grid, cube, ...) is built on top of the underlying 1d structure

;; Each window can be a member of any (positive) number of workspaces;
;; the `workspaces' property contains a list of workspace ids. Sticky
;; windows appear on all workspaces, and have their `sticky' property
;; set (with a null `workspaces' property)

;; When a window appears on more than one workspaces most of it's
;; properties are swapped in and out on demand when the current
;; workspace is changed. Use the `workspace-local-properties' and the
;; `workspace-swap-{in,out}-hook' variables (see workspace-swapper module)

;; If a window is added with its `workspaces' property set, then it's
;; added to those (logical) spaces

;; Private functions are prefixed by ws-

(eval-when-compile (require 'sawfish.wm.menus))

(define-structure sawfish.wm.workspace

    (export current-workspace
	    NormalState IconicState
	    window-workspaces
	    set-window-workspaces
	    window-in-workspace-p
	    map-window-workspaces
	    window-appears-in-workspace-p
	    transform-window-workspaces
	    workspace-empty-p
	    windows-share-workspace-p
	    all-workspaces
	    nearest-workspace-with-window
	    workspace-limits
	    workspace-id-to-logical
	    workspace-id-from-logical
	    move-window-to-workspace
	    copy-window-to-workspace
	    insert-workspace
	    remove-workspace
	    move-workspace
	    select-workspace
	    workspace-windows
	    workspace-menu
	    popup-workspace-list
	    popup-window-list
	    next-workspace
	    previous-workspace
	    send-to-next-workspace
	    send-to-previous-workspace
	    copy-to-next-workspace
	    copy-to-previous-workspace
	    merge-next-workspace
	    merge-previous-workspace
	    insert-workspace-before
	    insert-workspace-after
	    move-workspace-forwards
	    move-workspace-backwards
	    select-workspace-from-first
	    send-window-to-workspace-from-first
	    delete-empty-workspaces
	    delete-window-instance
	    add-swapped-properties
	    workspace-local-properties
	    set-number-of-workspaces
	    show-desktop
	    hide-desktop
	    showing-desktop-p

	    ;; XXX rename these..?
	    ws-remove-window
	    ws-add-window-to-space
	    ws-call-with-workspace)

    (open rep
	  rep.system
	  sawfish.wm.windows
	  sawfish.wm.misc
	  sawfish.wm.events
	  sawfish.wm.commands
	  sawfish.wm.custom
	  sawfish.wm.session.init)

;;; Options and variables

  (defcustom workspace-boundary-mode 'stop
    "How to act when passing the first or last workspace."
    :type (choice stop wrap-around keep-going)
    :group workspace)

  (defcustom workspace-send-boundary-mode 'stop
    "How to act when passing the first or last workspace, while moving a window"
    :type (choice stop wrap-around keep-going)
    :group workspace)

  (defcustom workspace-names nil
    nil
    :type* `(list string ,(_ "Workspace names"))
    :group workspace
    :widget-flags (expand-vertically)
    :after-set (lambda () (workspace-names-changed)))

  (defcustom lock-first-workspace t
    "Preserve outermost empty workspaces in the pager."
    :type boolean
    :group workspace)

  ;; Currently active workspace, an integer
  (define current-workspace 0)

  (define first-interesting-workspace nil)
  (define last-interesting-workspace nil)

  (defvar static-workspace-menus
    `((,(_ "_Insert workspace") insert-workspace-after)
      (,(_ "Select _next workspace") next-workspace)
      (,(_ "Select _previous workspace") previous-workspace)
      (,(_ "Merge with next") merge-next-workspace)
      (,(_ "Merge with previous") merge-previous-workspace)
      (,(_ "Move workspace _right") move-workspace-forwards)
      (,(_ "Move workspace _left") move-workspace-backwards)))

  ;; X constants
  (defconst NormalState 1)
  (defconst IconicState 3)

  (defvar workspace-swap-in-hook '())
  (defvar workspace-swap-out-hook '())

  (defvar enter-workspace-hook '())
  (defvar leave-workspace-hook '())
  (defvar workspace-state-change-hook '())
  (defvar add-to-workspace-hook '())
  (defvar remove-from-workspace-hook '())

  ;; window properties whose values may differ on different workspaces
  (define workspace-local-properties '())

  ;; true when in "show desktop" mode
  (define showing-desktop nil)

;;; Workspace ``swapping''

  ;; Property swapping is done on demand, and for each window
  ;; individually. This ensures that only the minimum required data is
  ;; ever swapped

  ;; Each window has a `swapped-in' property and may have a `swapped'
  ;; property. `swapped-in' is a workspace id defining which of the
  ;; window's configurations is stored in the window itself (as if there
  ;; was no swapping). The `swapped' property is an alist associating
  ;; workspace ids with an alist of swapped attributes 

  ;; this doesn't need to be called explicitly for every switch, just to
  ;; fork the data
  (define (swap-out w space)
    (let ((alist (apply nconc (mapcar (lambda (fun)
					(fun w space))
				      workspace-swap-out-hook)))
	  (swapped (assq space (window-get w 'swapped))))
      (if swapped
	  (rplacd swapped alist)
	(window-put w 'swapped (cons (cons space alist)
				     (window-get w 'swapped))))))

  ;; swap in data for workspace id SPACE to window W
  (define (swap-in w space)
    (let ((current (window-get w 'swapped-in)))
      (unless (eq current space)
	(when current
	  ;; swap out the current data
	  (swap-out w current))
	(let ((alist (assq space (window-get w 'swapped))))
	  (when alist
	    (call-hook 'workspace-swap-in-hook (list w space (cdr alist)))
	    (window-put w 'swapped (delq alist (window-get w 'swapped))))
	  (window-put w 'swapped-in space)))))

;;; Low level functions

  ;; return list of all workspaces containing window W
  (define (window-workspaces w)
    (window-get w 'workspaces))

  ;; set list of all workspaces containing window W to LST
  (define (set-window-workspaces w lst)
    (window-put w 'workspaces lst))

  ;; return t if window W is a member of workspace SPACE
  (define (window-in-workspace-p w space)
    (memq space (window-get w 'workspaces)))

  ;; map FUN over all workspace ids containing window W
  (define (map-window-workspaces fun w)
    (mapc fun (window-workspaces w)))

  (define (window-viewable-p w)
    (if showing-desktop
	(or (desktop-window-p w) (dock-window-p w))
      (not (window-get w 'iconified))))

  (define (window-appears-in-workspace-p w space)
    (or (window-get w 'sticky) (window-in-workspace-p w space)))

  ;; add window W to those in workspace SPACE
  (define (window-add-to-workspace w space)
    (set-window-workspaces
     w (cons space (delq space (window-workspaces w))))
    (cond ((= space current-workspace)
	   (swap-in w space))
	  ((and (window-get w 'swapped-in)
		(/= (window-get w 'swapped-in) space))
	   ;; write out the current state for when we switch
	   (swap-out w space))
	  (t
	   (window-put w 'swapped-in space))))

  ;; remove window W from those in workspace SPACE
  (define (window-remove-from-workspace w space)
    (set-window-workspaces w (delq space (window-workspaces w)))
    (window-put w 'swapped (delete-if (lambda (cell)
					(= (car cell) space))
				      (window-get w 'swapped)))
    (when (eq (window-get w 'swapped-in) space)
      (window-put w 'swapped-in nil)))

  ;; FUN is a function transforming an old workspace id to a new id.
  ;; Run this function over all workspaces that window W is a member of.
  (define (transform-window-workspaces fun w)
    (let ((workspaces (window-get w 'workspaces))
	  (swapped (window-get w 'swapped))
	  (swapped-in (window-get w 'swapped-in))
	  (new-workspaces '())
	  (new-swapped '()))
      (while workspaces
	(let* ((space (car workspaces))
	       (swap (assq space swapped))
	       (new (fun space)))
	  (unless (memq new new-workspaces)
	    (setq new-workspaces (cons new new-workspaces))
	    (when swap
	      (setq new-swapped (cons (cons new (cdr swap)) new-swapped))))
	  (when (eq swapped-in space)
	    (window-put w 'swapped-in new))
	  (setq workspaces (cdr workspaces))))
      (set-window-workspaces w (nreverse new-workspaces))
      (window-put w 'swapped (nreverse new-swapped))))

  ;; return t if workspace SPACE contains zero (non-sticky) windows
  (define (workspace-empty-p space)
    (catch 'out
      (map-windows (lambda (w)
		     (when (window-in-workspace-p w space)
		       (throw 'out nil))))
      t))

  ;; return t if windows W-1 and W-2 both appear on the same workspace
  (define (windows-share-workspace-p w-1 w-2)
    (if (or (window-get w-1 'sticky) (window-get w-2 'sticky))
	t
      (let ((spaces-1 (window-workspaces w-1))
	    (spaces-2 (window-workspaces w-2)))
	(catch 'out
	  (mapc (lambda (space)
		  (when (memq space spaces-2)
		    (throw 'out t))) spaces-1)
	  nil))))

  ;; return a list of all workspace indices that contain windows
  (define (all-workspaces)
    (let (spaces)
      (map-windows
       (lambda (w)
	 (map-window-workspaces (lambda (space)
				  (unless (memq space spaces)
				    (setq spaces (cons space spaces)))) w)))
      spaces))

  ;; return the nearest workspace to SPACE that contains window W
  (define (nearest-workspace-with-window w space)
    (if (window-get w 'sticky)
	space
      (let ((all (window-workspaces w))
	    (min-diff 10000)
	    (min-space nil)
	    tem)
	(while all
	  (setq tem (max (- space (car all)) (- (car all) space)))
	  (when (< tem min-diff)
	    (setq min-diff tem)
	    (setq min-space (car all)))
	  (setq all (cdr all)))
	min-space)))

  ;; returns (FIRST-INDEX . LAST-INDEX) defining the subset of the
  ;; continuum that is `interesting' to the user
  (define (workspace-limits)
    (let* ((all-spaces (all-workspaces))
	   (max-w (if last-interesting-workspace
		      (max last-interesting-workspace current-workspace)
		    current-workspace))
	   (min-w (if first-interesting-workspace
		      (min first-interesting-workspace current-workspace)
		    current-workspace)))
      (cond ((cdr all-spaces)
	     (setq max-w (max max-w (apply max all-spaces)))
	     (setq min-w (min min-w (apply min all-spaces))))
	    (all-spaces
	     (setq max-w (max max-w (car all-spaces)))
	     (setq min-w (min min-w (car all-spaces)))))
      (setq max-w (max max-w (1- (+ (length workspace-names) min-w))))
      (when lock-first-workspace
	(setq first-interesting-workspace min-w)
	(setq last-interesting-workspace max-w))
      (cons min-w max-w)))

  (define (set-number-of-workspaces wanted)
    (or (> wanted 0) (error "Too few workspaces: %s" wanted))
    (let* ((limits (workspace-limits))
	   (total (1+ (- (cdr limits) (car limits)))))
      (cond ((> total wanted)
	     ;; too many workspaces
	     (do ((i wanted (1+ i)))
		 ((>= i total))
	       (remove-workspace (car limits))))
	    ((< total wanted)
	     (setq first-interesting-workspace (car limits))
	     (setq last-interesting-workspace (1- (+ (car limits) wanted)))
	     (call-hook 'workspace-state-change-hook)))))

  (define (workspace-id-to-logical space #!optional limits)
    (unless limits
      (setq limits (workspace-limits)))
    (- space (car limits)))

  (define (workspace-id-from-logical offset #!optional limits)
    (unless limits
      (setq limits (workspace-limits)))
    (+ offset (car limits)))

  ;; renormalize the interesting workspaces so they begin at index zero
  (define (normalize-indices)
    (let* ((first-space (car (workspace-limits))))
      (map-windows (lambda (w)
		     (transform-window-workspaces (lambda (space)
						    (- space first-space)) w)))
      (setq current-workspace (- current-workspace first-space))
      (when first-interesting-workspace
	(setq first-interesting-workspace
	      (- first-interesting-workspace first-space)))
      (when last-interesting-workspace
	(setq last-interesting-workspace
	      (- last-interesting-workspace first-space)))))

  ;; insert a new workspace (returning its index) so that the workspace
  ;; before it has index BEFORE
  (define (insert-workspace #!optional before)
    (unless before
      (setq before current-workspace))
    (map-windows
     (lambda (w)
       (transform-window-workspaces (lambda (space)
				      (if (> space before)
					  (1+ space)
					space)) w)))
    (when (> current-workspace before)
      (setq current-workspace (1+ current-workspace)))
    (call-hook 'workspace-state-change-hook)
    (1+ before))

  ;; merge workspace INDEX with workspace INDEX+1
  (define (remove-workspace #!optional index)
    (unless index
      (setq index current-workspace))
    (when (> current-workspace index)
      (setq current-workspace (1- current-workspace)))
    (when (and first-interesting-workspace (> first-interesting-workspace index))
      (setq first-interesting-workspace (1- first-interesting-workspace)))
    (when (and last-interesting-workspace (> last-interesting-workspace index))
      (setq last-interesting-workspace (1- last-interesting-workspace)))
    (map-windows
     (lambda (w)
       (transform-window-workspaces (lambda (space)
				      (if (> space index)
					  (1- space)
					space)) w)
       (when (and (window-in-workspace-p w current-workspace)
		  (window-viewable-p w))
	 (show-window w))))
    (call-hook 'workspace-state-change-hook))

  ;; move workspace INDEX COUNT positions forwards (+ve or -ve)
  (define (move-workspace index count)
    (cond ((> count 0)
	   (map-windows
	    (lambda (w)
	      (transform-window-workspaces (lambda (space)
					     (cond ((< space index)
						    space)
						   ((= space index)
						    (+ space count))
						   ((<= space (+ index count))
						    (1- space))
						   (t space))) w)))
	   (cond ((= current-workspace index)
		  (setq current-workspace (+ current-workspace count)))
		 ((> current-workspace index)
		  (setq current-workspace (1- current-workspace)))))
	  ((< count 0)
	   (map-windows
	    (lambda (w)
	      (transform-window-workspaces (lambda (space)
					     (cond ((> space index)
						    space)
						   ((= space index)
						    (+ space count))
						   ((>= space (+ index count))
						  (1+ space))
						   (t space))) w)))
	   (cond ((= current-workspace index)
		  (setq current-workspace (+ current-workspace count)))
		 ((< current-workspace index)
		  (setq current-workspace (1+ current-workspace))))))
    (call-hook 'workspace-state-change-hook))

  ;; called when window W is destroyed
  (define (ws-remove-window w #!optional dont-hide)
    (let ((spaces (window-workspaces w)))
      (mapc (lambda (space)
	      (window-remove-from-workspace w space)) spaces)
      (when (and (not dont-hide) (windowp w))
	(hide-window w))
      (mapc (lambda (space)
	      (call-window-hook
	       'remove-from-workspace-hook w (list space))) spaces)
      (call-hook 'workspace-state-change-hook)))

  ;; move window W from workspace id OLD to workspace NEW
  (define (move-window-to-workspace w old new #!optional was-focused)
    (or (window-in-workspace-p w old)
	(error
	 "move-window-to-workspace--window isn't in original workspace: %s, %s" w old))
    (unless (= old new)
      (cond ((window-in-workspace-p w new)
	     ;; just remove from the source workspace
	     (window-remove-from-workspace w old)
	     (call-window-hook 'remove-from-workspace-hook w (list old)))
	    (t
	     ;; need to move it..
	     (transform-window-workspaces (lambda (space)
					    (if (= space old)
						new
					      space)) w)))
      (cond ((= old current-workspace)
	     (hide-window w))
	    ((and (= new current-workspace) (window-viewable-p w))
	     (show-window w)))
      ;; the window may lose the focus when switching spaces
      (when (and was-focused (window-visible-p w))
	(set-input-focus w))
      (call-window-hook 'add-to-workspace-hook w (list new))
      (call-hook 'workspace-state-change-hook)))

  ;; arrange it so that window W appears on both OLD and NEW workspaces
  (define (copy-window-to-workspace w old new #!optional was-focused)
    (or (window-in-workspace-p w old)
	(error
	 "copy-window-to-workspace--window isn't in original workspace: %s, %s" w old))
    (unless (= old new)
      (unless (window-in-workspace-p w new)
	(window-add-to-workspace w new))
      (when (and (= new current-workspace) (window-viewable-p w))
	(show-window w))
      ;; the window may lose the focus when switching spaces
      (when (and was-focused (window-visible-p w))
	(set-input-focus w))
      (call-window-hook 'add-to-workspace-hook w (list new))
      (call-hook 'workspace-state-change-hook)))

  ;; switch to workspace with id SPACE
  (define (select-workspace* space #!key dont-focus inner-thunk force)
    "Activate workspace number SPACE (from zero)."
    (when (or force (/= current-workspace space))
      (when current-workspace
	(call-hook 'leave-workspace-hook (list current-workspace)))
      (let ((order (stacking-order)))

	;; install the new workspace id
	(setq current-workspace space)

	;; this used to be called between unmapping old windows and
	;; mapping new windows, but we don't do that anymore. This
	;; should be ok though..
	(when inner-thunk
	  (inner-thunk))

	;; the server grabs are just for optimisation (each call to
	;; show-window or hide-window may also grab the server semaphore)
	(with-server-grabbed

	 ;; first map new windows top-to-bottom
	 (mapc (lambda (w)
		 (when (window-appears-in-workspace-p w current-workspace)
		   (swap-in w current-workspace))
		 (when (and (window-appears-in-workspace-p w current-workspace)
			    (window-get w 'placed))
		   (if (window-viewable-p w)
		       (show-window w)
		     (hide-window w))))
	       order)

	 ;; then unmap old-windows bottom-to-top
	 (mapc (lambda (w)
		 (when (and (not (window-appears-in-workspace-p
				  w current-workspace))
			    (window-get w 'placed))
		   (hide-window w)))
	       (nreverse order)))

	;; focus the correct window in the new workspace
	(unless (or dont-focus (eq focus-mode 'enter-exit))
	  (require 'sawfish.wm.util.window-order)
	  (window-order-focus-most-recent))

	;; and call some hooks
	(when current-workspace
	  (call-hook 'enter-workspace-hook (list current-workspace)))
	(call-hook 'workspace-state-change-hook))))

  (define (select-workspace space #!optional dont-focus inner-thunk)
    (select-workspace* space #:dont-focus dont-focus #:inner-thunk inner-thunk))

  ;; return a list of all windows on workspace index SPACE
  (define (workspace-windows
	   #!optional (space current-workspace) include-iconified)
    (filter-windows
     (lambda (w)
       (and (window-in-workspace-p w space)
	    (or include-iconified (not (window-get w 'iconified)))))))

  ;; add window W to workspace index SPACE; window shouldn't be in any
  ;; workspace
  (define (ws-add-window-to-space w space)
    (unless (window-get w 'sticky)
      (window-add-to-workspace w space)
      (cond ((not (window-viewable-p w))
	     (hide-window w))
	    ((= space current-workspace)
	     (show-window w))
	    ((not (window-in-workspace-p w current-workspace))
	     (hide-window w)))
      (call-window-hook 'add-to-workspace-hook w (list space))
      (call-hook 'workspace-state-change-hook)))

  ;; usually called from the add-window-hook; adds window W to the
  ;; current workspace (or wherever else it should go)
  (define (ws-add-window w)
    (ws-window-activated w)
    (if (window-get w 'sticky)
	(progn
	  (set-window-workspaces w nil)
	  (window-put w 'swapped-in current-workspace)
	  (if (window-viewable-p w)
	      (show-window w)
	    (hide-window w)))
      (if (window-workspaces w)
	  (let ((spaces (window-workspaces w))
		(limits (workspace-limits)))
	    (set-window-workspaces w '())
	    (mapc (lambda (space)
		    (ws-add-window-to-space
		     w (workspace-id-from-logical space limits))) spaces))
	;; add it to the current workspace
	(ws-add-window-to-space w current-workspace))))

  ;; called from the unmap-notify hook
  (define (ws-window-unmapped w)
    (unless (window-get w 'sticky)
      (ws-remove-window w t)))

  ;; called from the map-notify-hook
  (define (ws-window-mapped w)
    (unless (or (window-get w 'sticky) (window-workspaces w))
      (ws-add-window w)))

  (define (ws-window-activated w)
    (when (and showing-desktop
	       (not (or (desktop-window-p w) (dock-window-p w))))
      (hide-desktop)))

;;; Menu constructors

  (define (workspace-menu)
    (let ((limits (workspace-limits))
	  menu)
      (do ((i (car limits) (1+ i)))
	  ((> i (cdr limits)))
	(let ((ws-name (or (nth (- i (car limits)) workspace-names)
			   (format nil (_ "space %d")
				   (1+ (- i (car limits)))))))
	  (setq menu (cons (list (quote-menu-item ws-name)
				 (lambda () (select-workspace i))
				 (cons 'check (= i current-workspace))
				 '(group . current-workspace))
			   menu))))
      (nconc (nreverse menu) (list nil) static-workspace-menus)))

  (define (popup-workspace-list)
    "Display the menu containing the list of all workspaces."
    (require 'sawfish.wm.menus)
    (popup-menu (workspace-menu)))

  (define (popup-window-list)
    "Display the menu of all managed windows."
    (require 'sawfish.wm.menus)
    (declare (special window-menu))
    (popup-menu (window-menu)))

  (define-command 'popup-workspace-list popup-workspace-list)
  (define-command 'popup-window-list popup-window-list)

;;; Commands

  (define (ws-call-with-workspace fun count mode)
    (let ((limits (workspace-limits))
	  (target (+ current-workspace count)))
      (if (and (>= target (car limits)) (<= target (cdr limits)))
	  (fun target)
	(cond ((eq mode 'stop))
	      ((eq mode 'wrap-around)
	       (fun (+ (car limits)
		       (mod (- target (car limits))
			    (1+ (- (cdr limits) (car limits)))))))
	      ((eq mode 'keep-going)
	       (fun target))))))

  (define (next-workspace count)
    "Display the next workspace."
    (ws-call-with-workspace select-workspace count workspace-boundary-mode))

  (define (previous-workspace count)
    "Display the previous workspace."
    (next-workspace (- count)))

  (define-command 'next-workspace next-workspace #:spec "p")
  (define-command 'previous-workspace previous-workspace #:spec "p")

  (define (send-to-next-workspace w count #!optional copy select)
    "Move the window to the next workspace."
    (ws-call-with-workspace
     (lambda (space)
       (let ((was-focused (eq w (input-focus)))
	     (orig-space (if (window-in-workspace-p
			      w current-workspace)
			     current-workspace
			   (car (window-workspaces w)))))
	 (when orig-space
	   (copy-window-to-workspace w orig-space space was-focused)
	   (when select
	     (select-workspace space was-focused))
	   (unless copy
	     (move-window-to-workspace w orig-space space was-focused)))))
     count workspace-send-boundary-mode))

  (define (send-to-previous-workspace w count #!optional copy select)
    "Move the window to the previous workspace."
    (send-to-next-workspace w (- count) copy select))

  (define-command 'send-to-next-workspace send-to-next-workspace
    #:spec "%W\np\n\nt")
  (define-command 'send-to-previous-workspace send-to-previous-workspace
    #:spec "%W\np\n\nt")

  (define (copy-to-next-workspace w count select)
    "Copy the window to the next workspace."
    (send-to-next-workspace w count t select))

  (define (copy-to-previous-workspace w count #!optional select)
    "Copy the window to the previous workspace."
    (send-to-previous-workspace w count t select))

  (define-command 'copy-to-next-workspace copy-to-next-workspace
    #:spec "%W\np\nt")
  (define-command 'copy-to-previous-workspace copy-to-previous-workspace
    #:spec "%W\np\nt")

  (define (append-workspace-and-send w #!optional select)
    "Create a new workspace at the end of the list, and move the window to it."
    (let ((limits (workspace-limits))
	  (was-focused (eq (input-focus) w))
	  (orig-space (if (window-in-workspace-p w current-workspace)
			  current-workspace
			(car (window-workspaces w)))))
      (when orig-space
	(if select
	    (progn
	      (select-workspace (1+ (cdr limits)) was-focused)
	      (move-window-to-workspace
	       w orig-space current-workspace was-focused))
	  (move-window-to-workspace
	   w orig-space (1+ (cdr limits)) was-focused)))))

  (define (prepend-workspace-and-send w #!optional select)
    "Create a new workspace at the start of the list, and move the window to it."
    (let ((limits (workspace-limits))
	  (was-focused (eq (input-focus) w))
	  (orig-space (if (window-in-workspace-p w current-workspace)
			  current-workspace
			(car (window-workspaces w)))))
      (when orig-space
	(if select
	    (progn
	      (select-workspace (1- (car limits)) was-focused)
	      (move-window-to-workspace
	       w orig-space current-workspace was-focused))
	  (move-window-to-workspace
	   w orig-space (1- (car limits)) was-focused)))))

  (define-command 'append-workspace-and-send append-workspace-and-send
    #:spec "%W\nt" #:class 'advanced)
  (define-command 'prepend-workspace-and-send prepend-workspace-and-send
    #:spec "%W\nt" #:class 'advanced)

  (define (merge-next-workspace)
    "Delete the current workspace. Its member windows are relocated to the next
workspace."
    (remove-workspace current-workspace))

  (define (merge-previous-workspace)
    "Delete the current workspace. Its member windows are relocated to the
previous workspace."
    (remove-workspace (1- current-workspace)))

  (define-command 'merge-next-workspace merge-next-workspace
    #:class 'advanced)
  (define-command 'merge-previous-workspace merge-previous-workspace
    #:class 'advanced)

  (define (insert-workspace-after)
    "Create a new workspace following the current workspace."
    (insert-workspace current-workspace)
    (select-workspace (1+ current-workspace)))

  (define (insert-workspace-before)
    "Create a new workspace before the current workspace."
    (insert-workspace (1- current-workspace))
    (select-workspace (- current-workspace 2)))

  (define-command 'insert-workspace-after insert-workspace-after
    #:class 'advanced)
  (define-command 'insert-workspace-before insert-workspace-before
    #:class 'advanced)

  (define (move-workspace-forwards #!optional count)
    "Move the current workspace one place to the right."
    (move-workspace current-workspace (or count 1)))

  (define (move-workspace-backwards #!optional count)
    "Move the current workspace one place to the left."
    (move-workspace current-workspace (- (or count 1))))

  (define-command 'move-workspace-forwards move-workspace-forwards
    #:class 'advanced)
  (define-command 'move-workspace-backwards move-workspace-backwards
    #:class 'advanced)

  (define (select-workspace-from-first count)
    (select-workspace (workspace-id-from-logical count)))

  (define (send-window-to-workspace-from-first w count #!optional copy select)
    (let* ((was-focused (eq (input-focus) w))
	   (orig-space (if (window-in-workspace-p w current-workspace)
			   current-workspace
			 (car (window-workspaces w))))
	   (new-space (workspace-id-from-logical count)))
      (when (and orig-space (/= orig-space new-space))
	(copy-window-to-workspace w orig-space new-space was-focused)
	(when select
	  (select-workspace new-space was-focused))
	(unless copy
	  (move-window-to-workspace w orig-space new-space was-focused)))))

  (define (delete-empty-workspaces)
    "Delete any workspaces that don't contain any windows."
    (let* ((limits (workspace-limits))
	   (space (car limits)))
      (while (<= space (cdr limits))
	(if (workspace-empty-p space)
	    (cond ((= space (car limits))
		   (when (= first-interesting-workspace space)
		     (setq first-interesting-workspace (1+ space)))
		   (setq space (1+ space)))
		  ((= space (cdr limits))
		   (when (= last-interesting-workspace space)
		     (setq last-interesting-workspace (1- space)))
		   (setq space (1+ space)))
		  (t
		   (remove-workspace space)
		   (setq limits (workspace-limits))))
	  (setq space (1+ space))))
      (when (and first-interesting-workspace
		 (> first-interesting-workspace last-interesting-workspace))
	(setq first-interesting-workspace last-interesting-workspace))))

  (define-command 'delete-empty-workspaces delete-empty-workspaces
    #:class 'advanced)

  (define (delete-window-instance w)
    "Remove the copy of the window on the current workspace. If this is the
last instance remaining, then delete the actual window."
    (let ((spaces (window-workspaces w)))
      (if (cdr spaces)
	  ;; not the last instance
	  (let ((space (if (memq current-workspace spaces)
			   current-workspace
			 (car spaces))))
	    (window-remove-from-workspace w space)
	    (when (= space current-workspace)
	      (hide-window w))
	    (call-window-hook 'remove-from-workspace-hook w (list space))
	    (call-hook 'workspace-state-change-hook))
	(delete-window w))))

  (define-command 'delete-window-instance delete-window-instance #:spec "%W")

  (define (show-desktop)
    "Hide all windows except the desktop window."
    (unless showing-desktop
      (setq showing-desktop t)
      (select-workspace* current-workspace #:force t)))

  (define (hide-desktop)
    "Undoes the effect of the `show-desktop' command."
    (when showing-desktop
      (setq showing-desktop nil)
      (select-workspace* current-workspace #:force t)))

  (define (showing-desktop-p)
    "Returns true when in `showing desktop' mode."
    showing-desktop)

  (define-command 'show-desktop show-desktop)
  (define-command 'hide-desktop hide-desktop)

;; some commands for moving directly to a workspace

  (define (activate-workspace n)
    "Select the N'th workspace."
    (select-workspace-from-first (1- n)))

  (define-command 'activate-workspace activate-workspace
    #:spec "NWorkspace:"
    #:type `(and (labelled ,(_ "Workspace:") (number 1))))

  (define (send-to-workspace n)
    "Move the current window to the N'th workspace."
    (send-window-to-workspace-from-first (current-event-window) (1- n)))

  (define-command 'send-to-workspace send-to-workspace
    #:spec "NWorkspace:"
    #:type `(and (labelled ,(_ "Workspace:") (number 1))))

  (define (copy-to-workspace n)
    "Copy the current window to the N'th workspace."
    (send-window-to-workspace-from-first (current-event-window) (1- n) t))

  (define-command 'copy-to-workspace copy-to-workspace
    #:spec "NWorkspace:"
    #:type `(and (labelled ,(_ "Workspace:") (number 1))))

  (define (select-workspace-interactively)
    "Prompt for a workspace and switch to it."
    (require 'sawfish.wm.util.prompt)
    (let ((ws (prompt-for-workspace)))
      (when ws
	(select-workspace-from-first ws))))

  (define-command 'select-workspace-interactively
    select-workspace-interactively)

;;; session management

  (define (ws-saved-state w)
    (unless (window-get w 'sticky)
      (let ((limits (workspace-limits))
	    (spaces (window-workspaces w)))
	(when spaces
	  `((workspaces . ,(mapcar (lambda (space)
				     (workspace-id-to-logical space limits))
				   spaces)))))))

  (define (ws-load-state w alist)
    (cond ((cdr (assq 'workspaces alist))
	   (set-window-workspaces w (cdr (assq 'workspaces alist))))
	  ((cdr (assq 'workspace alist))
	   ;; backwards compatibility..
	   (set-window-workspaces w (list (cdr (assq 'workspace alist)))))))

  ;; Note that all of PROPS (symbols) should be saved and restored
  ;; automatically when swapping window states
  (define (add-swapped-properties #!rest props)
    (mapc (lambda (p)
	    (or (memq p workspace-local-properties)
		(setq workspace-local-properties
		      (cons p workspace-local-properties))))
	  props))

;;; configuration

  (define (workspace-names-changed)
    ;; XXX this isn't ideal, but it's better than getting
    ;; XXX workspaces that aren't deleted as the total
    ;; XXX number of workspaces is decreased...
    (setq first-interesting-workspace nil)
    (setq last-interesting-workspace nil)
    (call-hook 'workspace-state-change-hook))

;;; Initialisation

  (sm-add-saved-properties 'sticky 'iconified 'fixed-position)

  ;; some of these should really be added by other files
  (add-swapped-properties 'frame-style 'type)

  (add-hook 'add-window-hook ws-add-window)
  (add-hook 'map-notify-hook ws-window-mapped)
  (add-hook 'activate-window-hook ws-window-activated)
  (add-hook 'unmap-notify-hook ws-window-unmapped)
  (add-hook 'destroy-notify-hook ws-remove-window)
  (add-hook 'sm-window-save-functions ws-saved-state)
  (add-hook 'sm-restore-window-hook ws-load-state))
;; viewport.jl -- virtual desktops
;; $Id: viewport.jl,v 1.46 2002/04/23 03:44:18 jsh Exp $

;; Copyright (C) 1999 John Harper <john dcs warwick ac uk>

;; This file is part of sawmill.

;; sawmill is free software; you can redistribute it and/or modify it
;; under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; sawmill is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with sawmill; see the file COPYING.  If not, write to
;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

(define-structure sawfish.wm.viewport

    (export set-viewport
	    screen-viewport
	    set-screen-viewport
	    select-workspace-and-viewport
	    move-viewport
	    move-viewport-to-window
	    window-outside-workspace-p
	    window-outside-viewport-p
	    move-window-to-current-viewport
	    set-window-viewport
	    move-window-viewport
	    window-viewport
	    window-absolute-position
	    set-number-of-viewports)

    (open rep
	  rep.system
	  sawfish.wm.windows
	  sawfish.wm.misc
	  sawfish.wm.events
	  sawfish.wm.commands
	  sawfish.wm.workspace
	  sawfish.wm.custom
	  sawfish.wm.session.init)

  ;; Virtual workspaces are implemented by moving windows in and out of
  ;; the screen dimensions. E.g. moving to the left moves all windows one
  ;; screen-width to the right. 

  (defvar viewport-dimensions '(1 . 1)
    "Size of each virtual workspace.")

  (defvar uniconify-to-current-viewport t
    "Windows uniconify to the current viewport.")


;;; raw viewport handling

  (defvar viewport-x-offset 0)
  (defvar viewport-y-offset 0)

  (define (set-viewport x y)
    ;; move W to its new position
    (define (move-window w)
      (unless (window-get w 'sticky-viewport)
	(let ((pos (window-position w)))
	  (move-window-to w (- (+ (car pos) viewport-x-offset) x)
			  (- (+ (cdr pos) viewport-y-offset) y)))))

    (unless (and (= viewport-x-offset x) (= viewport-y-offset y))
      (let loop ((rest (stacking-order))
		 (inside '())
		 (outside '()))
	(cond ((null rest)
	       (with-server-grabbed
		;; First move all windows not on the old viewport, and
		;; move in top-to-bottom order..
		(mapc move-window (nreverse outside))
		;; ..then move away the windows on the old viewport,
		;; in bottom-to-top order
		(mapc move-window inside)))

	      ((window-outside-viewport-p (car rest))
	       (loop (cdr rest) inside (cons (car rest) outside)))

	      (t (loop (cdr rest) (cons (car rest) inside) outside))))

      (setq viewport-x-offset x)
      (setq viewport-y-offset y)
      (call-hook 'viewport-moved-hook)))

  (define (viewport-before-exiting)
    (set-screen-viewport 0 0))

  (add-hook 'before-exit-hook viewport-before-exiting t)


;; screen sized viewport handling

  (define (screen-viewport)
    (cons (quotient viewport-x-offset (screen-width))
	  (quotient viewport-y-offset (screen-height))))

  ;; returns t if it actually moved the viewport
  (define (set-screen-viewport col row)
    (when (and (>= col 0) (< col (car viewport-dimensions))
	       (>= row 0) (< row (cdr viewport-dimensions)))
      (set-viewport (* col (screen-width))
		    (* row (screen-height)))
      t))

  (define (select-workspace-and-viewport space col row)
    (select-workspace space nil (lambda ()
				  (set-screen-viewport col row))))
  
  ;; RIGHT and/or DOWN are 1 => at most one VP in that direction, 0 => no
  ;; movement, -1 => at most one VP in the opposite direction
  ;; ARG is () => go to next aligned VP in those directions, integer => go
  ;; <integer> pixels in those directions, (4 ^ integer) => go <integer>
  ;; times a quarter screen in those directions
  ;; returns t if it actually moved the viewport
  (define (move-viewport right down #!optional arg)
    (let ((w (screen-width))
	  (h (screen-height)))
      (cond
	((consp arg)
	 (require 'rep.lang.math)
	 (setq arg (inexact->exact (/ (log (prefix-numeric-argument arg))
				      (log 4)))
	       right (quotient (* right w arg) 4)
	       down (quotient (* down h arg) 4)))
	(arg
	 (setq arg (prefix-numeric-argument arg)
	       right (* right arg)
	       down (* down arg)))
	(t
	 (unless (zerop right)
	   (setq right (if (zerop (% viewport-x-offset w))
			   (* right w)
			 (- (modulo viewport-x-offset (* right w -1))))))
	 (unless (zerop down)
	   (setq down (if (zerop (% viewport-y-offset h))
			  (* down h)
			(- (modulo viewport-y-offset (* down h -1))))))))
      (setq right (+ right viewport-x-offset)
	    down (+ down viewport-y-offset))
      (when (and (<= 0 right (* w (1- (car viewport-dimensions))))
		 (<= 0 down (* h (1- (cdr viewport-dimensions)))))
	    (set-viewport right down)
	    t)))

  (define (move-viewport-to-window window)
    (when (window-outside-viewport-p window)
      (let ((pos (window-position window)))
	(rplaca pos (+ (car pos) viewport-x-offset))
	(rplacd pos (+ (cdr pos) viewport-y-offset))
	(set-screen-viewport (quotient (car pos) (screen-width))
			     (quotient (cdr pos) (screen-height))))))

  (define (window-outside-workspace-p window)
    (let ((pos (window-position window))
	  (dims (window-frame-dimensions window))
	  (left (- viewport-x-offset))
	  (right (- (* (car viewport-dimensions) (screen-width))
		    viewport-x-offset))
	  (top (- viewport-y-offset))
	  (bottom (- (* (cdr viewport-dimensions) (screen-height))
		     viewport-y-offset)))
      (or (>= (car pos) right)
	  (>= (cdr pos) bottom)
	  (<= (+ (car pos) (car dims)) left)
	  (<= (+ (cdr pos) (cdr dims)) top))))

  (define (window-outside-viewport-p window)
    (let ((pos (window-position window))
	  (dims (window-frame-dimensions window)))
      (or (<= (+ (car pos) (car dims)) 0)
	  (<= (+ (cdr pos) (cdr dims)) 0)
	  (>= (car pos) (screen-width))
	  (>= (cdr pos) (screen-height)))))

  (define (move-window-to-current-viewport window)
    (when (and (window-outside-viewport-p window)
	       (not (window-get window 'sticky-viewport)))
      (let ((pos (window-position window)))
	(move-window-to window (mod (car pos) (screen-width))
			(mod (cdr pos) (screen-height))))))

  (define (set-window-viewport window col row)
    (unless (window-get window 'sticky-viewport)
      (let ((pos (window-position window)))
	(setq col (max 0 (min (1- (car viewport-dimensions)) col)))
	(setq row (max 0 (min (1- (cdr viewport-dimensions)) row)))
	(setq col (+ (* col (screen-width)) (mod (car pos) (screen-width))))
	(setq row (+ (* row (screen-height)) (mod (cdr pos) (screen-height))))
	(move-window-to
	 window (- col viewport-x-offset) (- row viewport-y-offset)))))

  (define (move-window-viewport window col row)
    (let ((pos (window-position window)))
      (set-window-viewport window
			   (+ (quotient (+ (car pos) viewport-x-offset)
					(screen-width)) col)
			   (+ (quotient (+ (cdr pos) viewport-y-offset)
					(screen-height)) row))))

  (define (window-viewport w)
    (let ((position (window-position w)))
      (cons (quotient (+ (car position) viewport-x-offset) (screen-width))
	    (quotient (+ (cdr position) viewport-y-offset) (screen-height)))))

  (define (window-absolute-position w)
    (let ((position (window-position w)))
      (if (window-outside-viewport-p w)
	  (cons (mod (+ (car position) viewport-x-offset) (screen-width))
		(mod (+ (cdr position) viewport-y-offset) (screen-height)))
	position)))

  (define (viewport-size-changed)
    (let ((port (screen-viewport)))
      (set-screen-viewport (min (car port) (1- (car viewport-dimensions)))
			   (min (cdr port) (1- (cdr viewport-dimensions))))
      (map-windows (lambda (w)
		     (when (window-outside-workspace-p w)
		       (move-window-to-current-viewport w))))
      (call-hook 'viewport-resized-hook)))

  (define (set-number-of-viewports width height)
    (setq viewport-dimensions (cons width height))
    (viewport-size-changed))


;; commands

  (define (activate-viewport x y)
    "Select the specified viewport."
    (set-screen-viewport (1- x) (1- y)))

  (define-command 'activate-viewport activate-viewport
    #:spec "NX:\nNY:"
    #:type `(and (labelled ,(_ "Column:") (number 1))
		 (labelled ,(_ "Row:") (number 1)))
    #:class 'viewport)

  (define (activate-viewport-column x)
    "Select the specified viewport column."
    (set-screen-viewport (1- x) (cdr (screen-viewport))))

  (define-command 'activate-viewport-column activate-viewport-column
    #:spec "NX:"
    #:type `(and (labelled ,(_ "Column:") (number 1)))
    #:class 'viewport)

  (define (activate-viewport-row y)
    "Select the specified viewport row."
    (set-screen-viewport (car (screen-viewport)) (1- y)))

  (define-command 'activate-viewport-row activate-viewport-row
    #:spec "NY:"
    #:type `(and (labelled ,(_ "Row:") (number 1)))
    #:class 'viewport)

  (define (move-window-to-viewport x y)
    "Move the current window to the specified viewport."
    (move-window-viewport (current-event-window) (1- x) (1- y)))

  (define-command 'move-window-to-viewport move-window-to-viewport
    #:spec "NX:\nNY:"
    #:type '(and (labelled "X:" (number 1)) (labelled "Y:" (number 1)))
    #:class 'viewport)

  (define (move-viewport-right #!optional arg)
    "Move the viewport to the next screen or ARG pixels to the right.
With universal-argument move that many quarter screens."
    (move-viewport 1 0 arg))

  (define (move-viewport-left #!optional arg)
    "Move the viewport to the next screen or ARG pixels to the left.
With universal-argument move that many quarter screens."
    (move-viewport -1 0 arg))

  (define (move-viewport-down #!optional arg)
    "Move the viewport to the next screen or ARG pixels down.
With universal-argument move that many quarter screens."
    (move-viewport 0 1 arg))

  (define (move-viewport-up #!optional arg)
    "Move the viewport to the next screen or ARG pixels up.
With universal-argument move that many quarter screens."
    (move-viewport 0 -1 arg))

  ;; Moves the window by the specified offsets and then flips to the
  ;; viewport that is relative those offsets to the current viewport.
  (define (move-window-to-viewport-and-move-viewport window col row)
    (require 'sawfish.wm.util.stacking)
    (let ((sticky-viewport (window-get window 'sticky-viewport)))
      (window-put window 'sticky-viewport t)
      (with-server-grabbed
       (raise-window* window)
       (move-viewport col row))
      (unless sticky-viewport
	(window-put window 'sticky-viewport nil))))

  (define (move-window-left w)
    "Move the window to the viewport on the left, and switch to that viewport."
    (move-window-to-viewport-and-move-viewport w -1 0))

  (define (move-window-right w)
    "Move the window to the viewport on the right, and switch to that viewport."
    (move-window-to-viewport-and-move-viewport w 1 0))

  (define (move-window-down w)
    "Move the window to the viewport below, and switch to that viewport."
    (move-window-to-viewport-and-move-viewport w 0 1))

  (define (move-window-up w)
    "Move the window to the viewport above, and switch to that viewport."
    (move-window-to-viewport-and-move-viewport w 0 -1))

  (define-command 'move-viewport-right move-viewport-right #:spec "P" #:class 'viewport)
  (define-command 'move-viewport-left move-viewport-left #:spec "P" #:class 'viewport)
  (define-command 'move-viewport-up move-viewport-up #:spec "P" #:class 'viewport)
  (define-command 'move-viewport-down move-viewport-down #:spec "P" #:class 'viewport)
  (define-command 'move-window-right move-window-right #:spec "%W" #:class 'viewport)
  (define-command 'move-window-left move-window-left #:spec "%W" #:class 'viewport)
  (define-command 'move-window-up move-window-up #:spec "%W" #:class 'viewport)
  (define-command 'move-window-down move-window-down #:spec "%W" #:class 'viewport)


;;; session management, config

  (define (viewport-saved-state w)
    (let ((position (window-position w)))
      (when (window-get w 'sticky-viewport)
	(rplaca position (mod (car position) (screen-width)))
	(rplacd position (mod (cdr position) (screen-height))))
      `((position . ,(window-absolute-position w))
	(viewport . ,(window-viewport w)))))

  (define (viewport-load-state w alist)
    (let ((position (cdr (assq 'position alist)))
	  (viewport (cdr (assq 'viewport alist))))
      (when position
	(if (or (not viewport) (window-get w 'sticky-viewport))
	    (move-window-to w (car position) (cdr position))
	  (move-window-to w (+ (* (car viewport) (screen-width))
			       (car position)
			       (- viewport-x-offset))
			  (+ (* (cdr viewport) (screen-height))
			     (cdr position)
			     (- viewport-y-offset)))
	  (when (window-outside-workspace-p w)
	    (move-window-to-current-viewport w)))
	(window-put w 'placed t))))
			     
  (sm-add-saved-properties 'sticky-viewport)
  (add-hook 'sm-window-save-functions viewport-saved-state)
  (add-hook 'sm-restore-window-hook viewport-load-state)

  (define (viewport-window-uniconified w)
    (when uniconify-to-current-viewport
      (move-window-to-current-viewport w)))

  (add-hook 'uniconify-window-hook viewport-window-uniconified))


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]