new stuff
[emacs-init.git] / auto-install / framemove.el
diff --git a/auto-install/framemove.el b/auto-install/framemove.el
new file mode 100644 (file)
index 0000000..b1bf09b
--- /dev/null
@@ -0,0 +1,232 @@
+;;; framemove.el --- directional frame selection routines
+;;
+;; Copyright (C) 2010
+;;
+;; Author: Trey Jackson (bigfaceworm@gmail.com)
+;; Created: February 14, 2010
+;; Keywords: frame, movement, convenience
+;;
+;; This file is not (yet) a part of GNU Emacs.
+;;
+;; Very much like the windmove package, only for frames.
+;; Provide a simple set of keystrokes to move the input/focus
+;; between windows.
+;;
+;; Version 0.9
+;;
+;; This software is licensed under the GPL version 3.
+;;
+;; To install:
+;;   (require 'framemove)
+;;   (framemove-default-keybindings)
+;;
+;; If you want to integrate framemove and windmove
+;; You can omit the call to 'framemove-default-keybindings
+;; And instead do:
+;;    (require 'framemove)
+;;    (windmove-default-keybindings)
+;;    (setq framemove-hook-into-windmove t)
+;; 
+;; Compatibility: GNU Emacs 22.x, 23.x
+;;
+
+(defvar framemove-hook-into-windmove nil
+  "When non-nil, try moving frames if moving windows fails.")
+
+(defun fm-frame-bbox (frame)
+  ;; eval b/c when things are beyond borders, you get
+  ;; (+ -11) weirdness
+  (let ((yl (eval (frame-parameter frame 'top)))
+        (xl (eval (frame-parameter frame 'left))))
+    (list xl
+          yl
+          (+ xl (frame-pixel-width frame))
+          (+ yl (frame-pixel-height frame)))))
+
+(defun fm-opposite (dir)
+  (cdr (assq dir '((left . right) (right . left) (up . down) (down . up)))))
+
+(defun fm-frame-coord (frame-or-box dir)
+  (nth (cdr (assq dir '((left . 0) (up . 1) (right . 2) (down . 3))))
+       (if (framep frame-or-box)
+           (fm-frame-bbox frame-or-box)
+         frame-or-box)))
+
+(defun fm-frame-is-completly-to-dir-of (refframe dir otherframe)
+  (cond
+   ((eq refframe otherframe)
+    nil)
+   ((memq dir '(left up))
+    (< (fm-frame-coord refframe (fm-opposite dir)) (fm-frame-coord otherframe dir)))
+   ((memq dir '(right down))
+    (> (fm-frame-coord refframe (fm-opposite dir)) (fm-frame-coord otherframe dir)))
+   (t (error "Invalid direction of movement: %s" dir))))
+
+(defun fm-frame-is-to-dir-of (refframe dir otherframe)
+  (cond
+   ((not (eq (frame-parameter refframe 'display) (frame-parameter otherframe 'display)))
+      nil)
+   ((eq refframe otherframe)
+    nil)
+   ((memq dir '(left up))
+    (< (fm-frame-coord refframe dir) (fm-frame-coord otherframe dir)))
+   ((memq dir '(right down))
+    (> (fm-frame-coord refframe dir) (fm-frame-coord otherframe dir)))
+   (t (error "Invalid direction of movement: %s" dir))))
+
+(defun fm-absolute-coords-of-position (position)
+  (let ((rel-x-y (fm-frame-relative-coordinates position))
+        (frame-bbox (fm-frame-bbox (window-frame (posn-window position)))))
+    (cons (+ (car frame-bbox) (car rel-x-y))
+          (+ (cadr frame-bbox) (cdr rel-x-y)))))
+
+(defun fm-frame-relative-coordinates (position)
+  "Return frame-relative coordinates from POSITION."
+  (let* ((x-y (posn-x-y position))
+         (window (posn-window position))
+         (edges (window-inside-pixel-edges window)))
+    (cons (+ (car x-y) (car edges))
+          (+ (cdr x-y) (cadr edges)))))
+
+(defun fm-project (coord frame dir)
+  "project COORD in direction DIR to edge of FRAME"
+  (if (memq dir '(up down))
+      (cons (car coord)
+            (fm-frame-coord frame dir))
+    (cons (fm-frame-coord frame dir)
+          (cdr coord))))
+
+
+(defun fm-next-frame (dir)
+  "move focus to next frame in direction (from currently focused frame)"
+  (interactive (list
+                (intern (completing-read "Which direction: " '("up" "down" "left" "right") nil t))))
+  (let* ((thisframe (selected-frame))
+         (current-coords (fm-absolute-coords-of-position (posn-at-point)))
+         (coords-projected-in-dir (fm-project current-coords thisframe dir))
+         (possible-frames
+          (sort
+           (remove-if-not
+            '(lambda (f) (fm-frame-is-to-dir-of f dir thisframe))
+            (visible-frame-list))
+           '(lambda (f1 f2) (fm-frame-is-to-dir-of f1 (fm-opposite dir) f2)))))
+    (if possible-frames
+        (let ((frames-in-line-of-cursor
+               ;; try to find frame in line with cursor
+               (remove-if-not
+                '(lambda (f) (fm-coord-in-range current-coords dir f))
+                possible-frames))
+              (frames-in-line-of-frame
+               ;; find frame that overlaps current frame
+               ;; need to sort by distance from cursor
+               (sort
+                (remove-if-not
+                 '(lambda (f) (fm-range-overlap thisframe f dir))
+                 possible-frames)
+                '(lambda (f1 f2)
+                   (< (fm-dist-from-coords coords-projected-in-dir f1)
+                      (fm-dist-from-coords coords-projected-in-dir f2))))))
+          (select-frame-set-input-focus
+           (or (car frames-in-line-of-cursor)
+               (car frames-in-line-of-frame)
+               (car possible-frames))))
+      (error "No frame in that direction"))))
+
+(defun fm-dist-from-coords (coord frame)
+  "distance from coord to the bbox of the frame"
+  (let* ((x (car coord))
+         (y (cdr coord))
+         (x-in-range (fm-v-in-range x (fm-bbox-range 'left frame)))
+         (y-in-range (fm-v-in-range y (fm-bbox-range 'up frame)))
+         (x-dist (min (abs (- x (fm-frame-coord frame 'left)))
+                      (abs (- x (fm-frame-coord frame 'right)))))
+         (y-dist (min (abs (- y (fm-frame-coord frame 'up)))
+                      (abs (- y (fm-frame-coord frame 'down))))))
+    (cond ((and x-in-range y-in-range)
+           0)
+          (x-in-range
+           y-dist)
+          (y-in-range
+           x-dist)
+          ((sqrt (+ (expt x-dist 2)
+                    (expt y-dist 2)))))))
+              
+(defun fm-v-in-range (v range)
+  (and (> v (car range))
+       (< v (cdr range))))
+
+(defun fm-bbox-range (dir box)
+  (if (memq dir '(up down))
+      (cons (fm-frame-coord box 'up)
+            (fm-frame-coord box 'down))
+    (cons (fm-frame-coord box 'left)
+          (fm-frame-coord box 'right))))
+
+(defun fm-range-overlap (f1 f2 dir)
+  "return true if the bbox'es of the two frames overlap using coords perpendicular to dir"
+  (let ((perp (if (memq dir '(up down)) 'left 'up))
+        (f1box (fm-frame-bbox f1))
+        (f2box (fm-frame-bbox f2)))
+    (or (fm-v-in-range (fm-frame-coord f1 perp) (fm-bbox-range perp f2))
+        (fm-v-in-range (fm-frame-coord f1 (fm-opposite perp)) (fm-bbox-range perp f2))
+        (fm-v-in-range (fm-frame-coord f2 perp) (fm-bbox-range perp f1))
+        (fm-v-in-range (fm-frame-coord f2 (fm-opposite perp)) (fm-bbox-range perp f1)))))
+
+(defun fm-coord-in-range (coord dir frame)
+  "return true if the coord can be projected in orientation of dir
+onto the bbox of the frame, or more simply, is the part of the coord
+perpendicular to DIR between the edges of frame perpendicular to DIR"
+  (let ((n (if (memq dir '(up down)) (car coord) (cdr coord)))
+        (perp (if (memq dir '(up down)) 'left 'up)))
+    (and (< (fm-frame-coord frame perp) n)
+         (> (fm-frame-coord frame (fm-opposite perp)) n))))
+
+(defun fm-sort-frames-by-edge (framelist dir)
+  (sort
+   framelist
+   (lambda (f1 f2)
+     (apply (symbol-function
+             (if (memq dir '(left up)) '> '<))
+            (list (fm-frame-coord f1 dir) (fm-frame-coord f2 dir))))))
+
+;;;###autoload
+(defun fm-down-frame ()
+  (interactive)
+  (fm-next-frame 'down))
+;;;###autoload
+(defun fm-up-frame ()
+  (interactive)
+  (fm-next-frame 'up))
+;;;###autoload
+(defun fm-left-frame ()
+  (interactive)
+  (fm-next-frame 'left))
+;;;###autoload
+(defun fm-right-frame ()
+  (interactive)
+  (fm-next-frame 'right))
+
+;;;###autoload
+(defun framemove-default-keybindings (&optional modifier)
+  "Set up keybindings for `framemove'.
+Keybindings are of the form MODIFIER-{left,right,up,down}.
+Default MODIFIER is 'meta."
+  (interactive)
+  (unless modifier (setq modifier 'meta))
+
+  (global-set-key (vector (list modifier 'down))  'fm-down-frame)
+  (global-set-key (vector (list modifier 'up))    'fm-up-frame)
+  (global-set-key (vector (list modifier 'left))  'fm-left-frame)
+  (global-set-key (vector (list modifier 'right)) 'fm-right-frame))
+
+(defadvice windmove-do-window-select (around framemove-do-window-select-wrapper activate)
+  "Let windmove do its own thing, if there is an error, try framemove in that direction."
+  (condition-case err
+      ad-do-it
+    (error
+     (if framemove-hook-into-windmove
+         (fm-next-frame (ad-get-arg 0))
+       (error (error-message-string err))))))
+
+(provide 'framemove)
+;;; framemove.el ends here