;;; screenplay.el --- support writing of plain text screen plays

;; Copyright (C) 2002  Alex Schroeder <>

;; Author: Alex Schroeder <>
;; Maintainer: Alex Schroeder <>
;; Version: 1.0.0
;; Keywords: wp
;; URL:

;; This file is not part of GNU Emacs.

;; This 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 is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;;; Commentary:

;; This file contains code that will help you write screenplays in plain
;; text files.  Note that there is probably a LaTeX style out there that
;; will do the same thing for you, and look nicer.  But writing plain
;; text has its benefits: small, simple, easy to share, easy to mail.

;; Call `screenplay-reformat-region' or `screenplay-reformat-buffer' to
;; reformat your text.

;;; Code:

(defun screenplay-reformat-buffer ()
  "Reformat the buffer as a screenplay."
  (screenplay-reformat-region (point-min) (point-max)))

(defun screenplay-reformat-region (start end)
   "Reformat the region between START and END as a screenplay."
   (interactive "r")
     (goto-char start)
     (let ((end (copy-marker end))
           (format (screenplay-line-type))
           (old nil)
           (mark nil))
       (while (< (point) end)
         ;; handle filling if necessary
         (cond ((eq old 'description)
                (when (not (eq format 'description))
                  (screenplay-wrap mark (point) 0 65)
                  (setq mark nil)))
               ((eq old 'dialog)
                (when (not (eq format 'dialog))
                  (screenplay-wrap mark (point) 10 45)
                  (setq mark nil))))
         ;; indent lines, etc.
         (cond ((eq format 'empty)
                (kill-line 1)
                (forward-line -1))
               ((eq format 'instructions)
                (indent-to 16))
               ((eq format 'heading)
                (unless (bobp)
                  (newline 2)))
               ((eq format 'name)
                (newline 1)
                (indent-to 22))
               ((eq format 'dialog)
                (unless mark
                  (setq mark (point))))
               ((eq format 'description)
                (unless mark
                  (setq mark (point))))
               (t (error "Format %S unhandled" format)))
         (forward-line 1)
         (setq old format
               format (screenplay-line-type))))))

(defun screenplay-wrap (from to left right)
  "Fill region between FROM and TO.
LEFT and RIGHT are the margin columns used."
  (let ((left-margin left)
	(fill-column right)
	(to (copy-marker to)))
      (goto-char from)
      (while (< (point) to)
    (fill-region from to 'left)))

(defun screenplay-line-type ()
  "Return line type of current line."
    (let ((upcase (screenplay-upcase-p (point)))
	  (column-zero (looking-at "\\S-"))
	  (parens (looking-at "\\s-*(.*)\\s-*$"))
	  (empty (looking-at "\\s-*$")))
      (cond (parens 'instructions)
	    (empty 'empty)
	    ((and upcase column-zero) 'heading)
	    (upcase 'name)
	    (column-zero 'description)
	    (t 'dialog)))))

(defun screenplay-upcase-p (start)
  "Return t if no lower case characters appear between START
and the end of the line."
    (goto-char start)
    (let (quit found (c (char-after)))
      (while (not (or quit found))
	(cond ((eobp) (setq quit t))
	      ((eolp) (setq quit t))
	      ;; if the character has two different cases, and it is
	      ;; lowercase...
	      ((and (not (eq (downcase c) (upcase c)))
		    (eq c (downcase c)))
	       (setq found t))
	      (t (forward-char)
		 (setq c (char-after)))))
      (not found))))

(provide 'screenplay)
;;; screenplay.el ends here