;;; init.el --- Andrew Kensler's init.el File For GNU Emacs

;; Copyright (C) 1997-2009  Andrew Kensler

;; Author: Andrew Kensler
;; Version: 20090904
;; Last-Updated: Fri Sep  4 15:23:02 2009 (-0600)
;; Keywords: local, convenience

;; This file 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.
;;
;; This file 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 this file; see the file COPYING.  If not, write to the Free
;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
;; 02110-1301, USA.

;;; Commentary:

;; This is my personal startup file for GNU Emacs.  It should run well on
;; reasonably recent versions of both GNU Emacs and XEmacs, though I no
;; longer use XEmacs.  Some things may not be available depending on the
;; specific platform.

;;; Code:

;; ===========================================================================
;; Simple Settings and Internal Packages
;; ===========================================================================

;; Miscellaneous settings
;; ----------------------
;;
(setq-default inhibit-startup-screen t                        ; Skip the startup screens
              initial-scratch-message nil
              frame-title-format '(buffer-file-name "%f" "%b") ; I already know this is Emacs
              truncate-lines t                                ; Truncate lines, don't wrap
              default-truncate-lines t
              font-lock-use-fonts '(or (mono) (grayscale))    ; Turn on syntax hilighting
              font-lock-use-colors '(color)
              font-lock-maximum-decoration t
              font-lock-maximum-size nil
              font-lock-auto-fontify t
              global-font-lock-mode t
              paren-mode 'sexp                                ; Highlight parenthesis
              blink-cursor-alist '((t . hollow))              ; Cursor blinks solid and hollow
              user-full-name "Andrew Kensler"                 ; Set name
              user-mail-address "***@cs.utah.***"             ; Set e-mail address
              query-user-mail-address nil
              display-warning-minimum-level 'error            ; Turn off anoying warning messages
              disabled-command-function nil                   ; Don't second-guess advanced commands
              delete-key-deletes-forward t                    ; Make delete key work normally
              kill-read-only-ok t                             ; Silently copy in read-only buffers
              column-number-mode t                            ; Display line and column numbers
              line-number-mode t
              tab-width 4                                     ; Set tab stops
              tab-stop-list '(4 8 12 16 20 24 28 32 36 40 44
                              48 52 56 60 64 68 72 76 80 84)
              indent-tabs-mode nil                            ; Use spaces only, no tabs
              page-delimiter "^\\s *\n\\s *"                  ; Page delim one or more blank lines
              minibuffer-max-depth nil                        ; Mini-buffer settings
              toolbar-print-function 'ps-print-buffer-with-faces ; Set the print button to print nice PS
              ps-line-number t
              ps-n-up-printing 2
              ps-print-color-p nil
              fill-column 75                                  ; Wrap lines at 75th column
              initial-major-mode 'text-mode
              display-time-day-and-date t                     ; Display the time and date on the mode line
              case-fold-search t                              ; Fold case on searches
              buffers-menu-sort-function 'sort-buffers-menu-by-mode-then-alphabetically ; Buffers menu settings
              buffers-menu-grouping-function 'group-buffers-menu-by-mode-then-alphabetically
              buffers-menu-submenus-for-groups-p t
              buffers-tab-filter-functions nil                ; Buffers tab is flat
              ispell-program-name "aspell"                    ; Use aspell to spell check
              )
(defun startup-echo-area-message ()                           ; Use a more interesting startup message
  "By your command...")
(if (fboundp 'set-frame-parameter)                            ; Make the window 90% opaque in Carbon Emacs
    (set-frame-parameter nil 'alpha 90))
(if (fboundp 'set-scroll-bar-mode)                            ; Scrollbars should always be on the right
    (set-scroll-bar-mode 'right))
(if (fboundp 'set-fringe-mode)                                ; Turn off the left fringe
    (set-fringe-mode '(1 0)))
(display-time)                                                ; Display the time of day
(if (fboundp 'paren-set-mode)                                 ; Highlight the whole expression
    (paren-set-mode 'sexp)
  (setq show-paren-style 'expression)
  (show-paren-mode t))
(pending-delete-mode t)                                       ; Typed text replaces selection
(require 'pc-select)                                          ; Turn on PC selection mode if needed
(if (fboundp 'pc-select-mode)
    (pc-select-mode t))
(if (fboundp 'pc-selection-mode)
    (pc-selection-mode t))
(auto-fill-mode t)                                            ; Automatically wrap lines
(fset 'yes-or-no-p 'y-or-n-p)                                 ; Yes or no prompts accept short y or n
(require 'uniquify)                                           ; Smarter buffer naming than x<1>, x<2>, ...
(setq uniquify-buffer-name-style 'post-forward
      uniquify-after-kill-buffer-p t
      uniquify-ignore-buffers-re "^\\*")
(if (featurep 'mswindows)                                     ; If we're on Windows, write files with CR/LF
    (set-default-buffer-file-coding-system 'raw-text-dos))

;; Font lock customization
;; -----------------------
;; This is an attempt to provide a pleasing, sane and reasonably consistent
;; color scheme across GNU Emacs, XEmacs, Windows, X, and TTY systems.  It
;; also highlights trailing whitespace and fixme type tags.
;;
(require 'font-lock)
(if (fboundp 'global-font-lock-mode)
    (global-font-lock-mode t))
(make-face 'trailing-spaces-face "Face to display trailing spaces in.")
(add-hook 'font-lock-mode-hook                                ; Show trailing spaces and make fixme tags standout
          (lambda ()
            (font-lock-add-keywords nil
             '(("[ \t]+$" 0 'trailing-spaces-face t)
               ("AEK:?\\|FIXME:\\|TODO:\\|BUG:" 0 'font-lock-warning-face t)))))
(defun set-colour-theme (theme)
  "Helper function to set a bunch of faces and ignore potential errors from missing faces."
  (mapc (lambda (setting)
          (condition-case nil
              (face-spec-set (car setting) (cdr setting))
            (error t)))
        theme))
(defun light-on-dark-theme ()
  "Setup the colors for a light-on-dark theme."
  (interactive)
  (set-colour-theme
   '((default . ((((type tty)) (:background "black" :foreground "white")) (t (:background "black" :foreground "grey"))))
     (cursor . ((t (:background "plum"))))
     (modeline . ((((type tty)) (:inverse-video t)) (t (:foreground "black" :background "grey75" :box (:style released-button)))))
     (font-lock-keyword-face . ((t (:foreground "white" :bold t))))
     (font-lock-comment-face . ((((type tty)) (:foreground "cyan")) (t (:foreground "steelblue" :italic t))))
     (font-lock-string-face . ((((type tty)) (:foreground "green")) (t (:foreground "lightgreen"))))
     (font-lock-doc-string-face . ((((type tty)) (:foreground "green")) (t (:foreground "lightgreen"))))
     (font-lock-doc-face . ((((type tty)) (:foreground "green")) (t (:foreground "lightgreen"))))
     (font-lock-function-name-face . ((((type tty)) (:foreground "red" :bold t)) (t (:foreground "coral" :bold t))))
     (font-lock-type-face . ((((type tty)) (:foreground "cyan" :bold t)) (t (:foreground "steelblue" :bold t))))
     (font-lock-variable-name-face . ((((type tty)) (:foreground "magenta")) (t (:foreground "orchid"))))
     (font-lock-warning-face . ((t (:foreground "red" :bold t))))
     (font-lock-reference-face . ((((type tty)) (:foreground "red")) (t (:foreground "coral"))))
     (font-lock-builtin-face . ((((type tty)) (:foreground "red")) (t (:foreground "coral"))))
     (font-lock-constant-face . ((((type tty)) (:foreground "red")) (t (:foreground "coral"))))
     (paren-match . ((((type tty)) (:background "blue")) (t (:background "midnightblue"))))
     (show-paren-match-face . ((((type tty)) (:background "blue")) (t (:background "midnightblue"))))
     (zmacs-region . ((((type tty)) (:background "magenta")) (t (:foreground "black" :background "lightcoral"))))
     (region . ((((type tty)) (:background "magenta")) (t (:foreground "black" :background "lightcoral"))))
     (isearch . ((t (:foreground "white" :background "red"))))
     (isearch-secondary . ((((type tty)) (:foreground "red" :background "white")) (t (:foreground "red3" :background "grey"))))
     (isearch-lazy-highlight-face . ((((type tty)) (:foreground "red" :background "white")) (t (:foreground "red3" :background "grey"))))
     (trailing-spaces-face . ((((type tty)) (:background "grey")) (t (:background "grey15")))))))
(defun dark-on-light-theme ()
  "Setup the colors for a dark-on-light theme."
  (interactive)
  (set-colour-theme
   '((default . ((((type tty)) (:background "unspecified-bg" :foreground "unspecified-fg")) (t (:background "white" :foreground "black"))))
     (cursor . ((t (:background "red"))))
     (modeline . ((((type tty)) (:inverse-video t)) (t (:foreground "black" :background "grey75" :box (:style released-button)))))
     (font-lock-keyword-face . ((t (:foreground "black" :bold t))))
     (font-lock-comment-face . ((t (:foreground "blue" :italic t :underline nil))))
     (font-lock-string-face . ((((type tty)) (:foreground "green")) (t (:foreground "green4"))))
     (font-lock-doc-string-face . ((((type tty)) (:foreground "green")) (t (:foreground "green4"))))
     (font-lock-doc-face . ((((type tty)) (:foreground "green")) (t (:foreground "green4"))))
     (font-lock-function-name-face . ((t (:foreground "red" :bold t))))
     (font-lock-type-face . ((((type tty)) (:foreground "blue" :bold t)) (t (:foreground "steelblue" :bold t))))
     (font-lock-variable-name-face . ((t (:foreground "magenta"))))
     (font-lock-warning-face . ((t (:foreground "red" :bold t))))
     (font-lock-reference-face . ((((type tty)) (:foreground "red")) (t (:foreground "red3"))))
     (font-lock-builtin-face . ((((type tty)) (:foreground "red")) (t (:foreground "red3"))))
     (font-lock-constant-face . ((((type tty)) (:foreground "red")) (t (:foreground "red3"))))
     (paren-match . ((((type tty)) (:background "cyan")) (t (:background "lightsteelblue"))))
     (show-paren-match-face . ((((type tty)) (:background "cyan")) (t (:background "lightsteelblue"))))
     (zmacs-region . ((((type tty)) (:background "magenta")) (t (:background "lightcoral"))))
     (region . ((((type tty)) (:background "magenta")) (t (:background "lightcoral"))))
     (isearch . ((t (:foreground "white" :background "red"))))
     (isearch-secondary . ((((type tty)) (:foreground "red" :background "white")) (t (:foreground "red3" :background "grey"))))
     (isearch-lazy-highlight-face . ((((type tty)) (:foreground "red" :background "white")) (t (:foreground "red3" :background "grey"))))
     (trailing-spaces-face . ((((type tty)) (:background "magenta")) (t (:background "mistyrose")))))))
(dark-on-light-theme)

;; Extra key bindings
;; ------------------
;;
(define-key global-map [(control ?z)] nil)                    ; Appropriate C-z for a personal prefix key
(condition-case nil                                           ; Carbon XEmacs sets C-z as well
    (define-key global-window-system-map [(control ?z)] nil)
  (error t))
(condition-case nil
    (define-key global-tty-map [(control ?z)] nil)
  (error t))
(define-key global-map [(control ?z) ?d] 'dictionary-lookup-definition) ; C-z d looks up a word in the dictionary
(define-key global-map [(control ?z) ?f] 'auto-fill-mode)     ; C-z f toggles auto-fill
(define-key global-map [(control ?z) ?s] 'font-lock-fontify-buffer) ; C-z s updates syntax highlighting
(define-key global-map [(control ?z) ?l] 'sort-lines)         ; C-z l sorts the lines in a region
(define-key global-map [(control ?z) ?t] 'delete-trailing-whitespace) ; C-z t to clear trailing whitespace
(define-key global-map [(control ?z) tab] 'bury-buffer)       ; C-z TAB cycles through buffers
(define-key global-map [(control tab)] 'bury-buffer)          ; C-TAB also cycles through buffers
(define-key global-map [(meta ?g)] 'goto-line)                ; I like XEmacs' M-g for goto-line, use it GNU too
(define-key global-map [(control ?h) ?a] 'apropos)            ; Apropos *all* symbols, not just commands
(define-key global-map [(help)] 'overwrite-mode)              ; Some Macs have <help> instead of <insert>
(define-key global-map [(shift help)] 'yank)
(define-key global-map [(control help)] 'copy-region-as-kill)
(define-key global-map [(f13)] 'overwrite-mode)               ; Newer Macs have <fn> which can't be bound. Ugh!!
(define-key global-map [(shift f13)] 'yank)
(define-key global-map [(control f13)] 'copy-region-as-kill)

;; Clean Startup
;; -------------
;; I got tired of deleting the scratch and warning buffers that alway appear
;; after startup.  I prefer a clean, blank window when I begin.  This should
;; kill them after startup.
;;
(add-hook 'after-init-hook
          (lambda ()
            (let ((warnings (get-buffer "*Warnings*")))
              (if (not (equal warnings nil))
                  (kill-buffer warnings))))
          t)

;; Interactively Do Things
;; -----------------------
;; I've finally started using ido-mode for more efficient file finding and
;; buffer switching.  This is based on bits from Emacswiki that extend it
;; to most other places that take input from the minibuffer.
;;
(require 'ido)
(if (fboundp 'ido-mode)
    (progn
      (ido-mode t)
      (defadvice completing-read
        (around foo activate)
        (if (boundp 'ido-cur-list)
            ad-do-it
          (setq ad-return-value
                (ido-completing-read
                 prompt
                 (all-completions "" collection predicate)
                 nil require-match initial-input hist def))))
      (define-key global-map [(meta ?x)]
        (lambda ()
          (interactive)
          (call-interactively
           (intern
            (ido-completing-read "M-x " (all-completions "" obarray 'commandp))))))))

;; Text Settings
;; -------------
;; Basic, fundamental text mode settings.
;;
(add-hook 'text-mode-hook
          (lambda ()
            (auto-fill-mode t)))                              ; Fill and wrap automatically

;; C Style Modes
;; ------------
;; C mode is nice, but the GNU indent style bothers me.  This sets the
;; common hook so it applies to C, C++ and Java.  I define my own style
;; here rather than setting it to Ellemtel and then adjusting, because the
;; Ellemtel style seems slightly broken in the latest CVS Emacs.
;;
(add-hook 'c-mode-common-hook
          (lambda ()
            (c-add-style "andrew"                             ; Register and set to preferred style
                         '((c-basic-offset . 4)
                           (c-comment-only-line-offset . 0)
                           (c-hanging-braces-alist     . ((substatement-open before after)))
                           (c-offsets-alist . ((topmost-intro        . 0)
                                               (substatement         . +)
                                               (substatement-open    . 0)
                                                 (case-label           . +)
                                                 (access-label         . -)
                                                 (inclass              . +)
                                               (inline-open          . 0))))
             t)
            (c-toggle-auto-hungry-state 1)                    ; Turn on hungry delete
            (auto-fill-mode t)))                              ; Automatically wrap

;; CPerl Mode
;; ----------
;; Fill comment paragraphs in Perl.
;;
(add-hook 'cperl-mode-hook
          (lambda ()
            (auto-fill-mode t)                                ; Automatically wrap
            (setq-default cperl-continued-statement-offset 0  ; Fix Perl mode indentation
                          cperl-brace-offset 0
                          cperl-indent-level 4
                          cperl-lazy-help-time 1)))           ; Display one-line help on thing at point

;; Alignment
;; ---------
;; The align module provides handy functions for lining up columns of text
;; in source code.  The align module is sensitive to language syntax and
;; does a fairly good job of lining up variable and parameter declarations.
;; It's certainly handier than doing it by hand.  "align" is the primary
;; entry point.  NOTE: to align a space delimited table of text, make sure
;; it's in text mode, put a space before the first entry and do C-u before
;; invoking align.
;;
(add-hook 'align-load-hook
          (lambda ()
            (add-to-list 'align-c++-modes 'jde-mode)))        ; JDE mode should be treated as C++ like
(define-key global-map [(control ?z) ?a] 'align-current)      ; Bind to C-z a

;; ============================================================================
;; External E-Lisp Packages
;; ============================================================================

(add-to-list 'load-path "~/.emacs.d/")                        ; Append the .emacs.d dir to the path

;; notes-mode
;; ----------
;; I wrote this mode to make it easier to write up notes in a heirarchical
;; outline format.  Basically, it's to make it easier to edit my style of
;; notes.
;;
(autoload 'notes-mode "notes-mode" "Major mode for editing outline structured notes." t)

;; simple-calc
;; -----------
;; I wrote this as a lighter weight alternative to Calc.  It's a fairly
;; simple expression evaluator that uses a C-like syntax.
;;
(autoload 'simple-calc "simple-calc" "Simple calc is a simple, lightweight expression evaluator." t)
(define-key global-map [(control ?z) ?c] 'simple-calc)          ; Bind to C-z c

;; file-tree
;; ---------
;; I wrote this to be a handy light-weight display of the file system tree,
;; a bit like the Speedbar but better behaved on a TTY.
;;
(autoload 'file-tree "file-tree" "Tree display of files for easy navigation." t)
(define-key global-map [(control ?z) ?n] 'file-tree)          ; Bind to C-z n

;; lua-mode
;; --------
;; Lua is a neat little language.  Unfortunately, not all Emacs
;; distributions include lua-mode.
;;
(autoload 'lua-mode "lua-mode" "Lua editing mode." t)
(add-to-list 'auto-mode-alist '("\\.lua$" . lua-mode))

;; yasnippet
;; ---------
;; Yasnippet is a fairly slick new template mechanism.  It supports
;; intelligent mirrors, execution of arbitrary Emacs lisp code, reasonable
;; indenting and such.  The following is a mix of brand new snippets with
;; customized versions of a subset of the snippets that it comes bundled
;; with.  Unfortunately, it currently only works on GNU Emacs.
;;
(condition-case nil
    (progn (require 'yasnippet)
           (yas/initialize)
           (yas/load-directory "~/.emacs.d/snippets"))
  (error t))

;; htmlize
;; -------
;; XEmacs includes this by default, but not GNU Emacs
;;
(autoload 'htmlize-buffer "htmlize" "Convert buffer to HTML, preserving colors and decorations." t)

;; ============================================================================
;; Andrew's Custom Macros
;; ============================================================================

;; beginning-of-line-dynamic
;; -------------------------
;; Jumps to the beginning of text on the line, just like
;; beginning-of-line-text.  If it was already there, it jumps to the true
;; beginning of the line, before any space.  In other words, it toggles.
;; Basically, it's a smarter HOME command.
;;
(defun beginning-of-line-dynamic ()
  "Jumps to the beginning of text on line.  If already there, goes to the
true beginning of the line (before space.)"
  (interactive)
  (let ((cur (point)))
    (beginning-of-line-text)
    (if (= cur (point))
        (beginning-of-line))))
(define-key global-map [(home)] 'beginning-of-line-dynamic) ; Rebind HOME to our version

;; zap-up-to-char
;; --------------
;; Most of the time when I want to zap-to-char, it's to a delimeter that
;; I'd like to be left alone rather than zapped too.
;;
(defun zap-up-to-char (arg char)
  "Like standard zap-to-char, but stops just before the given character."
  (interactive "p\ncZap up to char: ")
  (kill-region (point)
               (progn
                 (search-forward (char-to-string char) nil nil arg)
                 (forward-char (if (>= arg 0) -1 1))
                 (point))))
(define-key global-map [(meta ?z)] 'zap-up-to-char)           ; Rebind M-z to our version

;; highlight-sloppy-grammar
;; ------------------------
;; This uses the font lock mechanism to highlight some potential
;; grammatical trouble spots.  It checks against a small list of common
;; problems such as duplicate words and instances of the passive voice.
;; It's not fool-proof but it does help when taking a pass over a paper.
;;
(defun highlight-sloppy-grammar ()
  "Highlight areas potentially containing sloppy grammar."
  (interactive)
  (make-face 'grammar-warning-face "Face to display grammar warnings in.")
  (face-spec-set 'grammar-warning-face
                 '((t (:bold t :foreground "orange" :underline t))))
  (font-lock-add-keywords nil
   '(("\\<\\(?:were\\|was\\|is\\|are\\|has been\\|be\\)\\(?:[ \t\r\n]+[a-zA-Z]+\\)?[ \t\r\n]+[a-zA-Z]+ed\\>"
      0 'grammar-warning-face t)
     ("\\<\\([a-zA-Z]+\\)[ \t\r\n]+\\1\\>" 0 'grammar-warning-face t)
     ("[,-][ \t\r\n]+that\\>" 0 'grammar-warning-face t)
     ("[a-zA-Z]+[ \t\r\n]+which\\>" 0 'grammar-warning-face t)
     ("\\<[a-z]+\\(?:n't\\|d've\\)\\>" 0 'grammar-warning-face t)
     ("\\<by[ \t\r\n]+[a-z]+ing\\>" 0 'grammar-warning-face t)
     ("\\<which[ \t\r\n]+was\\>" 0 'grammar-warning-face t)
     ("\\<the[ \t\r\n]+[a-zA-Z]+[ \t\r\n]+of[ \t\r\n]+the\\>" 0 'grammar-warning-face t)))
  (font-lock-fontify-buffer))

;; goto-longest-line
;; -----------------
;; Sometimes for code is nice to find lines that are pushed out too far.
;; This function moves point to the end of the longest line.  Also handy
;; for lining up columns of text when used in a narrowed buffer.
;;
(defun goto-longest-line ()
  "Finds the longest line and puts the point there."
  (interactive)
  (let ((width 0)
        (pos 0))
    (goto-char (point-min))
    (while (= (forward-line 1) 0)
      (end-of-line)
      (let ((curwid (current-column)))
        (unless (<= curwid width)
          (setq width curwid)
          (setq pos (point)))))
    (goto-char pos)))
(define-key global-map [(control ?z) ?g] 'goto-longest-line)  ; Bind to C-z g

;; goto-matching-paren
;; -------------------
;; If point is sitting on a parenthetic character, jump to its match.
;; This matches the standard parenthesis highlighting for determining which
;; one it is sitting on.
;;
(defun goto-matching-paren ()
  "If point is sitting on a parenthetic character, jump to its match."
  (interactive)
  (cond ((looking-at "\\s\(") (forward-list 1))
        ((progn
           (backward-char 1)
           (looking-at "\\s\)")) (forward-char 1) (backward-list 1))))
(define-key global-map [(control ?z) ?p] 'goto-matching-paren) ; Bind to C-z p

;; execute-keyboard-macro-here
;; ---------------------------
;; When clicked, move point to the location clicked and execute the last
;; defined keyboard macro there.  Very handy for automating actions which
;; must be done many times but at user controlled places. (e.g. lowercasing
;; HTML tags.)
;;
(defun execute-keyboard-macro-here (event)
  "Move point and execute the currently defined macro."
  (interactive "e")
  (mouse-set-point event)
  (call-last-kbd-macro))
(define-key global-map [(meta mouse-2)] 'execute-keyboard-macro-here) ; Bind to M-mouse-2

;; kill-other-buffers
;; ------------------
;; I find that Emacs buffers multiply faster than rabbits.  They were
;; regenerating faster than I could kill them so I wrote this.  (The
;; original version was my first code in ELisp!)  Run this macro to kill
;; all but the active buffer and the unsplit the window if need be.
;;
(defun kill-other-buffers ()
  "Kill all buffers except the current and unsplit the window."
  (interactive)
  (mapc 'kill-buffer (delq (current-buffer) (buffer-list)))   ; Delete other buffers
  (delete-other-windows)                                      ; And then unsplit the current window...
  (delete-other-frames))                                      ; ...and remove other frames, too.
(define-key global-map [(control ?z) ?k] 'kill-other-buffers) ; Bind to C-z k

;; browse-selected-file
;; -------------------
;; Browse the URL or highlighted in the buffer, typically this will be a file
;; name or a URL.  If nothing is highlighted, run the file being visited.
;; This gets rid of a long standing annoyance of things like editing an
;; HTML file in Emacs and then having to go outside to invoke a browser to
;; preview it.  Now Emacs can do that for me directly.
;;
(defun browse-selected-file ()
  "Opens a file in a browser .  If a region is selected, the text of the
highlighted region will be used as the filename or URL to load.  If no
region is active try to browse to the file being visited."
  (interactive)
  (if (if (fboundp 'region-exists-p)
          (region-exists-p)
        (and transient-mark-mode mark-active))
      (browse-url (buffer-substring (save-excursion (goto-char (region-beginning))
                                                    (skip-chars-forward " \t\n\r")
                                                    (point))
                                    (save-excursion (goto-char (region-end))
                                                    (skip-chars-backward " \t\n\r")
                                                    (point))))
    (browse-url (buffer-file-name))))
(define-key global-map [(control ?z) ?o] 'browse-selected-file) ; Bind to C-z o

;; uniq-lines
;; ----------
;; This is something of a companion to the built-in sort-lines function.
;; Like the uniq utility, this removes duplicate lines from the region.  It
;; is best used after sorting a region.
;;
(defun uniq-lines (start end)
  "Removes duplicate lines from the selected region."
  (interactive "*r")
  (goto-char start)
  (beginning-of-line)
  (let ((last ""))
    (while (< (point) end)
      (let* ((bol (point))
             (eol (progn (end-of-line) (point)))
             (text (buffer-substring bol eol)))
        (forward-char)
        (if (string= last text)
            (delete-region bol (point))
          (setq last text))))))
(define-key global-map [(control ?z) ?u] 'uniq-lines)         ; Bind to C-z u

;; for-each
;; --------
;; Copy the active region multiple times, each time replacing all
;; occurences of a placeholder matching a regexp with an incremented
;; number.  Useful for creating numbered list.  Use this multiple times
;; with different place holders for multi-dimensional expansions.
;;
(defun for-each (begin end place start stop step)
  "Copy the active region and replace a placeholder string with a number."
  (interactive "r\nsPlaceholder: \nn Start: \nn Stop: \nn Step: ")
  (let ((template (buffer-substring begin end))
        (count (1+ (/ (- stop start) step))))
    (delete-region begin end)
    (dotimes (time count)
      (let ((value (number-to-string (+ start (* time step))))
            (expansion template))
        (while (string-match place expansion)
          (setq expansion (replace-match value nil t expansion)))
        (insert expansion)))))
(define-key global-map [(control ?z) ?e] 'for-each)           ; Bind to C-z e

;; space-to-column
;; ---------------
;; Insert spaces until the point reaches a particular column, specified via
;; the universal argument.  If no column is given then it uses the current
;; goal column.  This does nothing if the point is already passed the goal
;; column.  Useful for quickly lining up columns of text via key macros.
;;
(defun space-to-column (col)
  "Insert spaces until the given column is reached."
  (interactive "P")
  (insert (make-string (max 0 (- (if col
                                     col
                                   goal-column)
                                 (current-column))) ? )))
(define-key global-map [(control ?z) ? ] 'space-to-column)    ; Bind to C-z SPC

;; wrap-expression
;; ---------------
;; Adds newlines and indents after operators at the current level of
;; parenthesization.  Useful for quickly breaking up long expressions over
;; multiple lines in a C-like language.  Note that it only handles
;; punctuation type operators and is likely to get confused by unaries.
;;
(defun wrap-expression ()
  "Wrap the current expression after operators at the current
level of parenthesization."
  (interactive)
  (save-excursion
    (goto-char (scan-lists (point) -1 1))
    (forward-char)
    (while (progn
             (skip-syntax-forward " w_\"\\$'<>p")
             (let ((after (char-after)))
               (cond ((= (char-syntax after) ?.)
                      (skip-syntax-forward ".")
                      (unless (or (= after ?.)
                                  (= (char-after) ?\n))
                        (newline-and-indent)
                        (skip-syntax-forward " .")))
                   ((= (char-syntax after) ?\()
                    (forward-list 1))))))))
(define-key global-map [(control ?z) ?b] 'wrap-expression)    ; Bind to C-z b

;; increment-time-stamp
;; --------------------
;; This helps with inserting and updating time stamps.  It's more general
;; than the built in time stamp stuff in that it doesn't look for any
;; particular keyword preceeding the time stamp.
;;
(defun increment-time-stamp ()
  "Search for something like a time stamp and increment it to the current time.
If a time stamp can't be found, then insert one at point."
  (interactive)
  (let ((stamp (format-time-string "%a %b %e %T %Y (%z)"))
        (found t))
    (save-excursion
      (goto-char (point-min))
      (if (re-search-forward
           (concat "\\(?:Mon\\|Tue\\|Wed\\|Thu\\|Fri\\|Sat\\|Sun\\) "
                   "\\(?:Jan\\|Feb\\|Mar\\|Apr\\|May\\|Jun\\|Jul\\|Aug\\|Sep\\|Oct\\|Nov\\|Dec\\) "
                   "[0-9 ][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] [0-9]\\{4\\} ([+-]?[0-9]+?)")
           nil t)
          (replace-match stamp t t)
        (setq found nil)))
    (unless found
      (insert stamp))))
(define-key global-map [(control ?z) ?i] 'increment-time-stamp) ; Bind to C-z i

;; toggle-mini-emacs
;; ------------------
;; Toggles the menubar, toolbar and tabs off.  Makes a minimalist Emacs.
;;
(defun toggle-mini-emacs ()
  "Switches back and forth between a minimal look."
  (interactive)
  (if (fboundp 'specifier-instance)
      (let ((setting (not (specifier-instance menubar-visible-p))))
        (set-specifier menubar-visible-p setting)
        (set-specifier top-toolbar-visible-p setting)
        (set-specifier top-gutter-visible-p setting))
    (if (fboundp 'menu-bar-mode)
        (let ((setting (not menu-bar-mode)))
          (menu-bar-mode setting)
          (tool-bar-mode setting)))))
(define-key global-map [(control ?z) ?m] 'toggle-mini-emacs) ; Bind to C-z m

;; toggle-zoom
;; -----------
;; Toggles a zoom to fullscreen on some platforms.  Good for hiding clutter
;; and getting things done without distractions.
;;
(defun toggle-zoom ()
  "Toggle zooming to full screen."
  (interactive)
  (if (fboundp 'set-frame-parameter)
      (set-frame-parameter nil 'fullscreen
                           (unless (frame-parameter nil 'fullscreen)
                             'fullboth))))
(define-key global-map [(control ?z) ?z] 'toggle-zoom) ; Bind to C-z z

;; show-ascii-chart
;; ----------------
;; Display a helpful ASCII reference chart when called.  Useful for quickly
;; double checking or looking up character codes.  Usually the
;; what-cursor-position (C-x =) is faster for spot lookups of the number
;; for a character here and there.  It's terrible, however, for finding the
;; character given a number.
;;
(defun show-ascii-chart ()
  "Display a helpful ASCII chart."
  (interactive)
  (let ((chart (concat
                "==============================================================================\n"
                "                        Common ASCII Codes And Escapes\n"
                "==============================================================================\n"
                "Char  Dec Hex Oct Esc Name             | Char  Dec Hex Oct Esc Name\n"
                "------------------------------------------------------------------------------\n"
                "(nul)   0  00 000 \\0  Null             | (np)   12  0c 014 \\f  Form Feed\n"
                "(bel)   7  07 007 \\a  Audible Alert    | (cr)   13  0d 015 \\r  Carriage Return\n"
                "(bs)    8  08 010 \\b  Backspace        | (sp)   32  20 040     Space\n"
                "(ht)    9  09 011 \\t  Horizontal Tab   | 0      48  30 060     Zero\n"
                "(nl)   10  0a 012 \\n  New Line         | A      65  41 101     Capital A\n"
                "(vt)   11  0b 013 \\v  Vertical Tab     | a      97  61 141     Lowercase a\n"
                "\n"
                "=============================================================================\n"
                "                                 ASCII Table\n"
                "=============================================================================\n"
                "Char  Dec Hex Oct | Char  Dec Hex Oct | Char  Dec Hex Oct | Char  Dec Hex Oct\n"
                "-----------------------------------------------------------------------------\n"
                "(nul)   0  00 000 | (sp)   32  20 040 | @      64  40 100 | `      96  60 140\n"
                "(soh)   1  01 001 | !      33  21 041 | A      65  41 101 | a      97  61 141\n"
                "(stx)   2  02 002 | \"      34  22 042 | B      66  42 102 | b      98  62 142\n"
                "(etx)   3  03 003 | #      35  23 043 | C      67  43 103 | c      99  63 143\n"
                "(eot)   4  04 004 | $      36  24 044 | D      68  44 104 | d     100  64 144\n"
                "(enq)   5  05 005 | %      37  25 045 | E      69  45 105 | e     101  65 145\n"
                "(ack)   6  06 006 | &      38  26 046 | F      70  46 106 | f     102  66 146\n"
                "(bel)   7  07 007 | '      39  27 047 | G      71  47 107 | g     103  67 147\n"
                "(bs)    8  08 010 | (      40  28 050 | H      72  48 110 | h     104  68 150\n"
                "(ht)    9  09 011 | )      41  29 051 | I      73  49 111 | i     105  69 151\n"
                "(nl)   10  0a 012 | *      42  2a 052 | J      74  4a 112 | j     106  6a 152\n"
                "(vt)   11  0b 013 | +      43  2b 053 | K      75  4b 113 | k     107  6b 153\n"
                "(np)   12  0c 014 | ,      44  2c 054 | L      76  4c 114 | l     108  6c 154\n"
                "(cr)   13  0d 015 | -      45  2d 055 | M      77  4d 115 | m     109  6d 155\n"
                "(so)   14  0e 016 | .      46  2e 056 | N      78  4e 116 | n     110  6e 156\n"
                "(si)   15  0f 017 | /      47  2f 057 | O      79  4f 117 | o     111  6f 157\n"
                "(dle)  16  10 020 | 0      48  30 060 | P      80  50 120 | p     112  70 160\n"
                "(dc1)  17  11 021 | 1      49  31 061 | Q      81  51 121 | q     113  71 161\n"
                "(dc2)  18  12 022 | 2      50  32 062 | R      82  52 122 | r     114  72 162\n"
                "(dc3)  19  13 023 | 3      51  33 063 | S      83  53 123 | s     115  73 163\n"
                "(dc4)  20  14 024 | 4      52  34 064 | T      84  54 124 | t     116  74 164\n"
                "(nak)  21  15 025 | 5      53  35 065 | U      85  55 125 | u     117  75 165\n"
                "(syn)  22  16 026 | 6      54  36 066 | V      86  56 126 | v     118  76 166\n"
                "(etb)  23  17 027 | 7      55  37 067 | W      87  57 127 | w     119  77 167\n"
                "(can)  24  18 030 | 8      56  38 070 | X      88  58 130 | x     120  78 170\n"
                "(em)   25  19 031 | 9      57  39 071 | Y      89  59 131 | y     121  79 171\n"
                "(sub)  26  1a 032 | :      58  3a 072 | Z      90  5a 132 | z     122  7a 172\n"
                "(esc)  27  1b 033 | ;      59  3b 073 | [      91  5b 133 | {     123  7b 173\n"
                "(fs)   28  1c 034 | <      60  3c 074 | \\      92  5c 134 | |     124  7c 174\n"
                "(gs)   29  1d 035 | =      61  3d 075 | ]      93  5d 135 | }     125  7d 175\n"
                "(rs)   30  1e 036 | >      62  3e 076 | ^      94  5e 136 | ~     126  7e 176\n"
                "(us)   31  1f 037 | ?      63  3f 077 | _      95  5f 137 | (del) 127  7f 177\n")))
    (if (fboundp 'with-displaying-help-buffer)
        (with-displaying-help-buffer
         (lambda ()
           (princ chart))
         "ASCII Chart")
      (with-output-to-temp-buffer "ASCII Chart"
        (princ chart)))))

;; point-stack
;; -----------
;; Alternative *simple* mechanism for recording points and buffers and
;; switching back to them later.  Points are stored on a stack with push
;; and pop bound to really easy keys for quick use.  If something heavier
;; duty is needed, use registers (C-x r SPC and C-x r j).
;;
(defvar point-stack nil
  "Stack to store point locations")
(defun push-point ()
  "Push the current point onto the stack."
  (interactive)
  (setq point-stack (cons (point-marker) point-stack)))
(define-key global-map [(control ?z) ?,] 'push-point)         ; Bind to C-z ,
(defun pop-point ()
  "Try to pop a point from the stack and return to it."
  (interactive)
  (if (not (null point-stack))
      (let ((m (car point-stack)))
        (setq point-stack (cdr point-stack))
        (if (not (marker-buffer m))
            (pop-point)
          (switch-to-buffer (marker-buffer m))
          (goto-char m)))))
(define-key global-map [(control ?z) ?.] 'pop-point)          ; Bind to C-z .

;; font-size
;; ---------
;; Sometimes you just feel like a larger, bolder font.  Other times you
;; want something a little smaller to get the big picture.  These adjust
;; the current display font size by a step in either direction.
;;
(defun font-increase ()
  "Make the current display font size a step larger."
  (interactive)
  (set-face-attribute 'default nil
                      :height (+ (face-attribute 'default :height) 10))
  (message "Font size: %s" (face-attribute 'default :height)))
(define-key global-map [(control ?z) ?=] 'font-increase)      ; Bind to C-z =
(defun font-decrease ()
  "Make the current display font size a step smaller."
  (interactive)
  (set-face-attribute 'default nil
                      :height (- (face-attribute 'default :height) 10))
  (message "Font size: %s" (face-attribute 'default :height)))
(define-key global-map [(control ?z) ?-] 'font-decrease)      ; Bind to C-z -

;; ===========================================================================
;; The Menu
;; ===========================================================================

;; This adds a small menu for my little macros up above and other
;; additions.  This makes it relatively easy to access them in
;; Emacs (although the hotkeys are certainly more efficient.)
;;
(let ((menu '("Andrew"
              ["Kill Other Buffers" kill-other-buffers]
              ["Open In Browser" browse-selected-file]
              ["Mini Emacs" toggle-mini-emacs]
              ["Zoom to Fullscreen" toggle-zoom]
              ["ASCII Chart" show-ascii-chart]
              ["Highlight Sloppy Grammar" highlight-sloppy-grammar]
              ["Simple Calc" simple-calc]
              ["Speedbar" speedbar]
              ("Align"
               ["Align" align]
               ["Align On Regexp" align-regexp]
               ["Align As One Section" align-entire]
               ["Align Current Section" align-current])
              ("Fonts"
               ["Consolas" (set-face-attribute 'default nil :family "Consolas")]
               ["Inconsolata" (set-face-attribute 'default nil :family "Inconsolata")]
               ["Monaco" (set-face-attribute 'default nil :family "Monaco")]
               ["DejaVu" (set-face-attribute 'default nil :family "DejaVu Sans Mono")]
               ["Basilisk" (set-face-attribute 'default nil :family "Basilisk")])
              ("Colours"
               ["Light-on-dark" light-on-dark-theme]
               ["Dark-on-light" dark-on-light-theme]))))
  (if (fboundp 'add-submenu)
      (add-submenu nil menu)
    (require 'easymenu)
    (easy-menu-define andrews-menu global-map "Andrew's Personal Menu" menu)
    (easy-menu-add andrews-menu global-map)))

;; Local Variables:
;; mode: emacs-lisp
;; comment-column: 64
;; End:
;;; init.el ends here