home *** CD-ROM | disk | FTP | other *** search
- ;;!emacs
- ;;
- ;; FILE: kview.el
- ;; SUMMARY: Display handling of koutlines.
- ;; USAGE: GNU Emacs Lisp Library
- ;; KEYWORDS: outlines, wp
- ;;
- ;; AUTHOR: Bob Weiner & Kellie Clark
- ;;
- ;; ORIG-DATE: 6/30/93
- ;; LAST-MOD: 25-Aug-95 at 00:31:48 by Bob Weiner
- ;;
- ;; This file is part of Hyperbole.
- ;; Available for use and distribution under the same terms as GNU Emacs.
- ;;
- ;; Copyright (C) 1993-1995, Free Software Foundation, Inc.
- ;; Developed with support from Motorola Inc.
- ;;
- ;; DESCRIPTION:
- ;; DESCRIP-END.
-
- ;;; ************************************************************************
- ;;; Other required Lisp Libraries
- ;;; ************************************************************************
- (mapcar 'require '(klabel kfill hypb))
-
- ;;; ************************************************************************
- ;;; Public variables
- ;;; ************************************************************************
-
- (set-default 'kview nil)
-
- (defvar kview:default-label-min-width 4
- "*Minimum width to which to pad labels in a kotl view.
- Labels are padded with spaces on the left.")
-
- (defvar kview:default-label-separator " "
- "*Default string of characters to insert between label and contents of a kotl cell view.")
-
- (defvar kview:default-label-type 'alpha
- "*Default label-type to use for new views.
- It must be one of the following symbols:
- no for no labels
- id for permanent idstamp labels, e.g. 001, 002, etc.
- alpha for '1a2' full alphanumeric labels
- legal for '1.1.2' labels
- partial-alpha for partial alphanumeric labels, e.g. '2' for node '1a2'
- star for multi-star labeling, e.g. '***'.")
-
- (defvar kview:default-level-indent 3
- "*Default number of spaces to indent each succeeding level in kotl views.")
-
- ;;; ************************************************************************
- ;;; Public functions
- ;;; ************************************************************************
-
- ;;;
- ;;; kcell-view
- ;;;
-
- (defun kcell-view:backward (&optional visible-p label-sep-len)
- "Move to start of the prior cell at the same level as the current cell.
- With optional VISIBLE-P, consider only visible cells.
- Return t unless no such cell."
- (or label-sep-len (setq label-sep-len
- (kview:label-separator-length kview)))
- (let ((opoint (point))
- (found) (done)
- (curr-indent 0)
- (start-indent (kcell-view:indent nil label-sep-len)))
- (while (and (not (or found done))
- (kcell-view:previous visible-p label-sep-len))
- (if (bobp)
- (progn (setq done t)
- (goto-char opoint))
- (setq curr-indent (kcell-view:indent nil label-sep-len))
- (cond ((= curr-indent start-indent)
- (goto-char (kcell-view:start nil label-sep-len))
- (setq found t))
- ((< curr-indent start-indent)
- ;; Went past start of this tree without a match.
- (setq done t)
- (goto-char opoint))
- ;; else go to prior node
- )))
- found))
-
- (defun kview:beginning-of-actual-line ()
- "Go to the beginning of the current line whether collapsed or not."
- (if (re-search-backward "[\n\^M]" nil 'move)
- (forward-char 1)))
-
- (defun kcell-view:cell (&optional pos)
- "Return kcell at optional POS or point."
- (kproperty:get (kcell-view:plist-point pos) 'kcell))
-
- (defun kcell-view:child (&optional label-sep-len)
- "Move to start of current cell's child within current view.
- Return t unless cell has no child.
- Optional LABEL-SEP-LEN is the length of the separation between
- a cell's label and the start of its contents."
- (let* ((opoint (point))
- (prev-indent (kcell-view:indent nil label-sep-len))
- (next (kcell-view:next nil label-sep-len)))
- (or label-sep-len (setq label-sep-len
- (kview:label-separator-length kview)))
- ;; Since kcell-view:next leaves point at the start of a cell, the cell's
- ;; indent is just the current-column of point.
- (if (and next (> (current-column) prev-indent))
- t
- ;; Move back to previous point and return nil.
- (goto-char opoint)
- nil)))
-
- (defun kcell-view:collapse (&optional pos label-sep-len)
- "Collapse cell at optional POS or point within the current view."
- (save-excursion
- (goto-char (kcell-view:start pos label-sep-len))
- (subst-char-in-region (point) (kcell-view:end-contents) ?\n ?\^M t)))
-
- (defun kcell-view:collapsed-p (&optional pos label-sep-len)
- "Return t if cell at optional POS or point is collapsed within the current view."
- (save-excursion
- (goto-char (kcell-view:start pos label-sep-len))
- (if (search-forward "\^M" (kcell-view:end-contents) t)
- t)))
-
- (defun kcell-view:contents ()
- "Return contents of current kview cell as a string."
- (let ((indent (kcell-view:indent))
- (start (kcell-view:start))
- (end (kcell-view:end-contents)))
- ;; Remove indentation from all but first line.
- (hypb:replace-match-string
- (concat "\\([\n\^M]\\)" (make-string indent ?\ ))
- (buffer-substring start end) "\\1")))
-
- (defun kcell-view:create (kview cell level klabel &optional no-fill)
- "Insert into KVIEW at point, CELL at LEVEL (1 = first level) with KLABEL.
- Optional NO-FILL non-nil suppresses filling of cell's contents upon insertion."
- (if (= (kcell:idstamp cell) 0)
- nil
- (let* ((label-min-width (kview:label-min-width kview))
- (label-fmt (format "%%%ds" label-min-width))
- (label (if (string= klabel "") "" (format label-fmt klabel)))
- (label-separator (if (string= klabel "") " "
- (kview:label-separator kview)))
- (mult-line-indent (* (1- level) (kview:level-indent kview)))
- (thru-label (+ mult-line-indent label-min-width
- (length label-separator)))
- (old-point (point))
- (fill-prefix (make-string thru-label ?\ ))
- contents
- new-point)
- (if no-fill (kcell:set-attr cell 'no-fill t))
- (insert fill-prefix)
- (setq contents (kview:insert-contents cell nil no-fill))
- ;; Insert lines to separate cell from next.
- (insert (if (or no-fill (equal contents ""))
- "\n\n" "\n"))
- (kfile:narrow-to-kcells)
- (setq new-point (point))
- (goto-char old-point)
- ;; Delete leading spaces used to get fill right in first cell
- ;; line. Replace it with label.
- (delete-char thru-label)
- (insert (format
- (format "%%%ds" (- thru-label (length label-separator)))
- label))
- (setq old-point (point))
- (insert label-separator)
- (goto-char old-point)
- ;; Add cell's properties to the text property list at point.
- (kproperty:set 'kcell cell)
- (goto-char new-point))))
-
- (defun kcell-view:end (&optional pos)
- "Return end position of cell from optional POS or point.
- Includes blank lines following cell contents."
- (or pos (setq pos (point)))
- (save-excursion
- (or (re-search-forward "[\n\^M][\n\^M]" nil t)
- (point-max))))
-
- (defun kcell-view:end-contents (&optional pos)
- "Return end position of cell contents from optional POS or point.
- Excludes blank lines following cell contents."
- (save-excursion
- (if pos (goto-char pos))
- (goto-char (kcell-view:end))
- (skip-chars-backward "\n\^M")
- (point)))
-
- (defun kcell-view:expand (&optional pos label-sep-len)
- "Expand cell at optional POS or point within the current view."
- (save-excursion
- (goto-char (kcell-view:start pos label-sep-len))
- (subst-char-in-region (point) (kcell-view:end-contents) ?\^M ?\n t)))
-
- (defun kcell-view:forward (&optional visible-p label-sep-len)
- "Move to start of the following cell at the same level as the current cell.
- With optional VISIBLE-P, consider only visible cells.
- Return t unless no such cell."
- (or label-sep-len (setq label-sep-len
- (kview:label-separator-length kview)))
- (let ((opoint (point))
- (found) (done)
- (curr-indent 0)
- (start-indent (kcell-view:indent nil label-sep-len)))
- (while (and (not (or found done))
- (kcell-view:next visible-p label-sep-len))
- (setq curr-indent (kcell-view:indent nil label-sep-len))
- (cond ((= curr-indent start-indent)
- (goto-char (kcell-view:start nil label-sep-len))
- (setq found t))
- ((< curr-indent start-indent)
- ;; Went past end of this tree without a match.
- (setq done t)
- (goto-char opoint))
- ;; else go to following node
- ))
- ;; If didn't find a match, return to original point.
- (or found (goto-char opoint))
- found))
-
- (defun kcell-view:get-attr (attribute &optional pos)
- "Get ATTRIBUTE's value for current cell or cell at optional POS."
- (interactive "SAttribute to get: ")
- (save-excursion
- (if pos (goto-char pos))
- (let ((value (kcell:get-attr (kcell-view:cell) attribute)))
- (if (interactive-p)
- (message "Attribute %s = %s, in cell <%s>."
- attribute value (kcell-view:label)))
- value)))
-
- (defun kcell-view:idstamp (&optional pos)
- "Return idstamp string of cell at optional POS or point."
- (save-excursion
- (if pos (goto-char pos))
- (format "0%d" (kcell:idstamp (kcell-view:cell)))))
-
- (defun kcell-view:indent (&optional pos label-sep-len)
- "Return indentation of cell at optional POS or point.
- Optional LABEL-SEP-LEN is the view-specific length of the separator between a
- cell's label and the start of its contents."
- (or (+ (save-excursion
- (kcell-view:to-label-end pos)
- (current-column))
- (or label-sep-len (kview:label-separator-length kview)))
- (error "(kcell-view:indent): No internal cell properties at %s"
- (or pos "point"))))
-
- (defun kcell-view:label (&optional pos)
- "Return displayed label string of cell at optional POS or point.
- If labels are off, return cell's idstamp as a string."
- (save-excursion
- (if pos (goto-char pos))
- (let ((label-type (kview:label-type kview)))
- (if (eq label-type 'no)
- (kcell-view:idstamp)
- (kcell-view:to-label-end)
- (buffer-substring (point) (progn (skip-chars-backward "^ \t\n\^M")
- (point)))))))
-
- (defun kcell-view:level (&optional pos label-sep-len indent)
- "Return cell level relative to top cell of the outline for current cell or one at optional POS.
- 0 = top cell level, 1 = 1st level in outline.
- Optional LABEL-SEP-LEN is length of spaces between a cell label and its the
- start of its body in the current view. Optional INDENT is the indentation in
- characters of the cell whose level is desired."
- (or label-sep-len (setq label-sep-len (kview:label-separator-length kview)))
- (save-excursion
- (if pos (goto-char pos))
- (/ (- (or indent (kcell-view:indent nil label-sep-len)) label-sep-len)
- (kview:level-indent kview))))
-
- (defun kcell-view:line (&optional pos)
- "Return contents of cell line at point or optional POS as a string."
- (save-excursion
- (if pos (goto-char pos))
- (if (kview:valid-position-p)
- (buffer-substring
- (kotl-mode:beginning-of-line)
- (kotl-mode:end-of-line))
- (error "(kcell-view:line): Invalid position, '%d'" (point)))))
-
- (defun kcell-view:next (&optional visible-p label-sep-len)
- "Move to start of next cell within current view.
- With optional VISIBLE-P, consider only visible cells.
- Return t unless no next cell."
- (let ((opoint (point))
- pos)
- ;;
- ;; If a subtree is collapsed, be sure we end up at the start of a visible
- ;; cell rather than within an invisible one.
- (if visible-p
- (progn (goto-char (kcell-view:end-contents)) (end-of-line)))
- (setq pos (kproperty:next-single-change (point) 'kcell))
- (if (or (null pos)
- (if (goto-char pos) (kotl-mode:eobp)))
- (progn (goto-char opoint)
- nil)
- (goto-char (kcell-view:start nil label-sep-len))
- (not (eq opoint (point))))))
-
- (defun kcell-view:operate (function &optional start end)
- "Invoke FUNCTION with view restricted to current cell contents.
- Optional START and END are start and endpoints of cell to use."
- (save-restriction
- (narrow-to-region (or start (kcell-view:start))
- (or end (kcell-view:end-contents)))
- (funcall function)))
-
- (defun kcell-view:parent (&optional visible-p label-sep-len)
- "Move to start of current cell's parent within current view.
- If parent is top cell, move to first cell within view and return 0.
- Otherwise, return t unless optional VISIBLE-P is non-nil and the parent cell
- is not part of the current view."
- (or label-sep-len (setq label-sep-len (kview:label-separator-length kview)))
- (let ((opoint (point))
- (parent-level (1- (kcell-view:level nil label-sep-len))))
- (if (= parent-level 0) ;; top cell
- (progn (goto-char (point-min))
- (goto-char (kcell-view:start nil label-sep-len))
- 0)
- ;; Skip from point back past any siblings
- (while (kcell-view:backward visible-p label-sep-len))
- ;; Move back to parent.
- (if (kcell-view:previous visible-p label-sep-len)
- t
- ;; Move back to previous point and return nil.
- (goto-char opoint)
- nil))))
-
- (defun kcell-view:previous (&optional visible-p label-sep-len)
- "Move to start of previous cell within current view.
- With optional VISIBLE-P, consider only visible cells.
- Return t unless no previous cell."
- (let ((opoint (point))
- (pos (point)))
- (goto-char (kcell-view:start nil label-sep-len))
- ;;
- ;; If a subtree is collapsed, be sure we end up at the start of a visible
- ;; cell rather than within an invisible one.
- (if visible-p
- (beginning-of-line)
- (if (setq pos (kproperty:previous-single-change (point) 'kcell))
- (goto-char pos)))
- (if (and pos (not (kotl-mode:bobp))
- (setq pos (kproperty:previous-single-change (point) 'kcell)))
- (progn (goto-char pos)
- (skip-chars-backward "\n\^M")
- (goto-char (kcell-view:start nil label-sep-len))
- (not (eq opoint (point))))
- ;; No previous cell exists
- (goto-char opoint)
- nil)))
-
- (defun kcell-view:plist (&optional pos)
- "Return properties associated with cell at optional POS or point."
- (kcell:plist (kcell-view:cell pos)))
-
- (defun kcell-view:plist-point (&optional pos)
- "Return buffer position of properties associated with cell at optional POS or point."
- (save-excursion (1+ (kcell-view:to-label-end pos))))
-
- (defun kcell-view:to-label-end (&optional pos)
- "Move point after end of current cell's label and return point."
- (if pos (goto-char pos))
- (kview:end-of-actual-line)
- (cond ((null kview)
- (error "(kcell-view:to-label-end): Invalid kview; try {M-x kotl-mode RET} to fix it."))
- (klabel-type:changing-flag
- ;; When changing from one label type to another, e.g. alpha to
- ;; legal, we can't depend on the label being of the type given by
- ;; the kview, so use kcell properties to find label end.
- (if (kproperty:get (1- (point)) 'kcell)
- nil
- ;; If not at beginning of cell contents, move there.
- (goto-char (kproperty:previous-single-change (point) 'kcell)))
- ;; Then move to end of label via embedded kcell property.
- (goto-char (kproperty:previous-single-change (point) 'kcell)))
- ((funcall (kview:get-attr kview 'to-label-end))
- (point))
- (t (error "(kcell-view:to-label-end): Can't find end of current cell's label"))))
-
- (defun kcell-view:reference (&optional pos relative-dir)
- "Return a reference to the kcell at optional POS or point for use in a link.
- The reference is a list of: (kcell-file cell-ref) where cell-ref is a string
- consisting of the cell's relative id, a space, and the cell's permanent id.
- Kcell-file is made relative to optional RELATIVE-DIR before it is returned."
- (list (hpath:relative-to buffer-file-name relative-dir)
- (concat (kcell-view:label pos) "=" (kcell-view:idstamp pos))))
-
- (defun kcell-view:remove-attr (attribute &optional pos)
- "Remove ATTRIBUTE, if any, for current cell or cell at optional POS."
- (interactive "*SAttribute to remove: ")
- (save-excursion
- (if pos (goto-char pos))
- (let ((kcell (kcell:remove-attr (kcell-view:cell) attribute)))
- (if (interactive-p)
- (message "Cell <%s> now has no %s attribute."
- (kcell-view:label) attribute))
- kcell)))
-
- (defun kcell-view:set-attr (attribute value &optional pos)
- "Set ATTRIBUTE's VALUE for current cell or cell at optional POS."
- (interactive "*SAttribute to set: \nXSet value of %s to: ")
- (save-excursion
- (if pos (goto-char pos))
- (let ((kcell (kcell:set-attr (kcell-view:cell) attribute value)))
- (if (interactive-p)
- (message "Attribute %s set to %s, in cell <%s>."
- attribute value (kcell-view:label)))
- kcell)))
-
- (defun kcell-view:set-cell (kcell)
- "Attach KCELL property to cell at point."
- (save-excursion
- (kcell-view:to-label-end)
- (kproperty:set 'kcell kcell)))
-
- (defun kcell-view:sibling-p (&optional pos label-sep-len)
- "Return t if cell at optional POS or point has a sibling (whether visible or not)."
- (save-excursion
- (if pos (goto-char pos))
- (kcell-view:forward nil label-sep-len)))
-
- (defun kcell-view:start (&optional pos label-sep-len)
- "Return start position of cell contents from optional POS or point."
- (save-excursion
- (+ (kcell-view:to-label-end pos)
- (or label-sep-len (kview:label-separator-length kview)))))
-
- ;;;
- ;;; kview - one view per buffer, multiple views per kotl
- ;;;
-
- (defun kview:add-cell (klabel level &optional contents prop-list no-fill)
- "Create a new cell with full KLABEL and add it at point at LEVEL within outline.
- 1 = first level. Optional cell CONTENTS and PROP-LIST may also be given, as
- well as NO-FILL which skips filling of any CONTENTS.
- Return new cell."
- (let ((new-cell (kcell:create contents (kview:id-increment kview)
- prop-list)))
- (kcell-view:create kview new-cell level klabel no-fill)
- new-cell))
-
- (defun kview:buffer (kview)
- "Return kview's buffer or nil if argument is not a kview."
- (if (kview:is-p kview)
- (get-buffer (kview:get-attr kview 'view-buffer-name))))
-
- (defun kview:create (buffer-name
- &optional id-counter label-type level-indent
- label-separator label-min-width)
- "Return a new kview for BUFFER-NAME.
- Optional ID-COUNTER is the maximum permanent id previously given out in this
- outline. Optional LABEL-TYPE, LEVEL-INDENT, LABEL-SEPARATOR, and
- LABEL-MIN-WIDTH may also be given, otherwise default values are used.
-
- See documentation of:
- 'kview:default-label-type' for LABEL-TYPE,
- 'kview:default-level-indent' for LEVEL-INDENT,
- 'kview:default-label-separator' for LABEL-SEPARATOR,
- 'kview:default-label-min-width' for LABEL-MIN-WIDTH."
-
- (let ((buf (get-buffer buffer-name))
- )
- (cond ((null buf)
- (error "(kview:create): No such buffer, '%s'." buffer-name))
- ((or (null id-counter) (= id-counter 0))
- (setq id-counter 0))
- ((not (integerp id-counter))
- (error "(kview:create): 2nd arg, '%s', must be an integer." id-counter)))
- (set-buffer buf)
- (if (and (boundp 'kview) (eq (kview:buffer kview) buf))
- ;; Don't recreate view if it exists.
- nil
- (make-local-variable 'kview)
- (setq kview
- (list 'kview 'plist
- (list 'view-buffer-name buffer-name
- 'top-cell
- (kcell:create-top buffer-file-name id-counter)
- 'label-type (or label-type kview:default-label-type)
- 'label-min-width (or label-min-width
- kview:default-label-min-width)
- 'label-separator (or label-separator
- kview:default-label-separator)
- 'label-separator-length
- (length (or label-separator
- kview:default-label-separator))
- 'level-indent (or level-indent
- kview:default-level-indent))))
- (kview:set-functions (or label-type kview:default-label-type)))
- kview))
-
- ;;; Using this stimulates an GNU Emacs V19.19 bug in text-property handling,
- ;; visible when one deletes a sibling cell and then deletes the prior cell,
- ;; the following cell is left with a different idstamp and its label
- ;; displays as "0". Using delete-char here would solve the problem but we
- ;; suggest you upgrade to a newer version of GNU Emacs in which the bug is
- ;; fixed.
- (defun kview:delete-region (start end)
- "Delete cells between START and END points from current view."
- (delete-region start end))
-
- (defun kview:end-of-actual-line ()
- "Go to the end of the current line whether collapsed or not."
- (if (re-search-forward "[\n\^M]" nil 'move)
- (backward-char 1)))
-
- (defun kview:fill-region (start end &optional kcell justify)
- "Fill region between START and END within current view.
- With optional KCELL, assume START and END delimit that cell's contents.
- With optional JUSTIFY, justify region as well.
- Fill-prefix must be a string of spaces the length of this cell's indent, when
- this function is called."
- (let ((opoint (set-marker (make-marker) (point)))
- (label-sep-len (kview:label-separator-length kview))
- (continue t)
- prev-point)
- (goto-char start)
- (while continue
- (if (kcell:get-attr (or kcell (kcell-view:cell)) 'no-fill)
- (setq continue (kcell-view:next nil label-sep-len))
- (fill-paragraph justify t)
- (setq prev-point (point))
- (forward-paragraph)
- (re-search-forward "[^ \t\n\^M]" nil t))
- (setq continue (and continue
- (/= (point) prev-point)
- (< (point) (min end (point-max))))))
- ;; Return to original point.
- (goto-char opoint)
- (set-marker opoint nil)))
-
- (cond ((and hyperb:xemacs-p (or (>= emacs-minor-version 12)
- (> emacs-major-version 19)))
- (defun kview:goto-cell-id (id-string)
- "Move point to start of cell with idstamp ID-STRING and return t, else nil."
- (let ((cell-id (string-to-int id-string))
- label-end kcell)
- (setq label-end
- (map-extents
- (function (lambda (extent unused)
- (setq kcell (extent-property extent 'kcell))
- (if (= (kcell:idstamp kcell) cell-id)
- (extent-end-position extent))))
- nil nil nil nil nil 'kcell))
- (if (null label-end)
- nil
- (goto-char label-end)
- t))))
- (hyperb:lemacs-p
- (defun kview:goto-cell-id (id-string)
- "Move point to start of cell with idstamp ID-STRING and return t, else nil."
- (let ((cell-id (string-to-int id-string))
- label-end kcell)
- (setq label-end
- (map-extents
- (function (lambda (extent unused)
- (setq kcell (extent-property extent 'kcell))
- (and kcell (= (kcell:idstamp kcell) cell-id)
- (extent-end-position extent))))))
- (if (null label-end)
- nil
- (goto-char label-end)
- t))))
- ;; Emacs 19
- (t (defun kview:goto-cell-id (id-string)
- "Move point to start of cell with idstamp ID-STRING and return t, else nil."
- (let ((cell-id (string-to-int id-string))
- (opoint (point))
- pos kcell)
- (goto-char (point-min))
- (while (and (setq pos
- (kproperty:next-single-change (point) 'kcell))
- (goto-char pos)
- (or (null (setq kcell (kproperty:get pos 'kcell)))
- (/= (kcell:idstamp kcell) cell-id))))
- (if pos
- (progn
- (forward-char (kview:label-separator-length kview))
- t)
- (goto-char opoint)
- nil))))
- )
-
- (defun kview:id-increment (kview)
- "Return next idstamp (an integer) for KVIEW."
- (let* ((top-cell (kview:get-attr kview 'top-cell))
- (counter (1+ (kcell:get-attr top-cell 'id-counter))))
- (kcell:set-attr top-cell 'id-counter counter)
- counter))
-
- (defun kview:idstamp-to-label (permanent-id)
- "Return relative label for cell with PERMANENT-ID within current kview."
- (save-excursion
- (if (kotl-mode:goto-cell permanent-id)
- (kcell-view:label))))
-
- (defun kview:insert-contents (kcell contents no-fill)
- "Insert KCELL's CONTENTS into view at point and fill resulting paragraphs, unless NO-FILL is non-nil.
- If CONTENTS is nil, get contents from KCELL. Return contents inserted (this
- value may differ from the value passed in.)
-
- Fill-prefix must be a string of spaces the length of this cell's indent, when
- this function is called."
- (let ((start (point))
- end)
- (setq contents (or contents (kcell:contents kcell) ""))
- (insert contents)
- ;;
- ;; Delete any extra newlines at end of cell contents.
- (setq end (point))
- (skip-chars-backward "\n\^M")
- (delete-region (point) end)
- (setq end (point))
- ;;
- (if no-fill
- ;; Insert proper indent in all but the first line which has
- ;; already been indented.
- (progn
- (narrow-to-region start end)
- (goto-char (point-min))
- (while (re-search-forward "[\n\^M]" nil t)
- (insert fill-prefix))
- (goto-char (point-max)))
- ;;
- ;; Filling cell will insert proper indent on all lines.
- (if (equal contents "")
- nil
- (goto-char start)
- (beginning-of-line)
- (narrow-to-region (point) end)
- ;; Add fill-prefix to all but paragraph separator lines, so
- ;; filling is done properly.
- (while (re-search-forward "[\n\^M][^\n\^M]" nil t)
- (forward-char -1) (insert fill-prefix))
- (kview:fill-region start end kcell)
- (goto-char (point-min))
- ;; Now add fill-prefix to paragraph separator lines.
- (while (re-search-forward "[\n\^M][\n\^M]" nil t)
- (forward-char -1) (insert fill-prefix))
- ;;
- (goto-char (point-max))))
- contents))
-
- (defun kview:is-p (object)
- "Is OBJECT a kview?"
- (and (listp object) (eq (car object) 'kview)))
-
- (defun kview:kotl (kview)
- "Return kview's kotl object or nil if argument is not a kview."
- (if (kview:is-p kview)
- (kview:get-attr kview 'kotl)))
-
- (defun kview:label (klabel-function prev-label child-p)
- "Return label string to display for current cell computed from KLABEL-FUNCTION, PREV-LABEL and CHILD-P."
- (funcall klabel-function prev-label child-p))
-
- (defun kview:label-function (kview)
- "Return function which will return display label for current cell in KVIEW.
- Function signature is: (func prev-label &optional child-p), where prev-label
- is the display label of the cell preceding the current one and child-p is
- non-nil if cell is to be the child of the preceding cell."
- (kview:get-attr kview 'label-function))
-
- (defun kview:label-min-width (kview)
- "Return kview's label-min-width setting or nil if argument is not a kview.
- See documentation for kview:default-label-min-width."
- (if (kview:is-p kview)
- (kview:get-attr kview 'label-min-width)))
-
- (defun kview:label-separator (kview)
- "Return kview's label-separator setting or nil if argument is not a kview.
- See documentation for kview:default-label-separator."
- (if (kview:is-p kview)
- (kview:get-attr kview 'label-separator)))
-
- (defun kview:label-separator-length (kview)
- "Return kview's label-separator length or nil if argument is not a kview.
- See documentation for kview:default-label-separator."
- (kview:get-attr kview 'label-separator-length))
-
- (defun kview:label-type (kview)
- "Return kview's label-type setting or nil if argument is not a kview.
- See documentation for kview:default-label-type."
- (if (kview:is-p kview)
- (kview:get-attr kview 'label-type)))
-
- (defun kview:level-indent (kview)
- "Return kview's level-indent setting or nil if argument is not a kview.
- See documentation for kview:default-level-indent."
- (if (kview:is-p kview)
- (kview:get-attr kview 'level-indent)))
-
- (defun kview:map-branch (func kview &optional first-p visible-p)
- "Applies FUNC to the sibling trees from point forward within KVIEW and returns results as a list.
- With optional FIRST-P non-nil, begins with first sibling in current branch.
- With optional VISIBLE-P, considers only those sibling cells that are visible
- in the view.
-
- FUNC should take one argument, the kview local variable of the current
- buffer or some other kview, and should operate upon the cell at point.
-
- `Cell-indent' contains the indentation value of the first cell mapped when
- FUNC is called so that it may test against this value. `Label-sep-len'
- contains the label separator length.
-
- See also 'kview:map-siblings' and 'kview:map-tree'."
- (save-excursion
- (set-buffer (kview:buffer kview))
- (let ((results)
- (label-sep-len (kview:label-separator-length kview)))
- (if first-p
- ;; Move back to first predecessor at same level.
- (while (kcell-view:backward t label-sep-len)))
- (let ((cell-indent (kcell-view:indent nil label-sep-len)))
- ;; Terminate when no further cells or when reach a cell at an equal
- ;; or higher level in the kotl than the first cell that we processed.
- (while (and (progn (setq results (cons (funcall func kview) results))
- (kcell-view:next visible-p label-sep-len))
- (> (kcell-view:indent nil label-sep-len) cell-indent))))
- (nreverse results))))
-
- (defun kview:map-siblings (func kview &optional first-p visible-p)
- "Applies FUNC to the sibling cells from point forward within KVIEW and returns results as a list.
- With optional FIRST-P non-nil, begins with first sibling in current branch.
- With optional VISIBLE-P, considers only those sibling cells that are visible
- in the view.
-
- FUNC should take one argument, the kview local variable of the current
- buffer or some other kview, and should operate upon the cell at point.
-
- `Cell-indent' contains the indentation value of the first cell mapped when
- FUNC is called so that it may test against this value. `Label-sep-len'
- contains the label separator length.
-
- See also 'kview:map-branch' and 'kview:map-tree'."
- (save-excursion
- (set-buffer (kview:buffer kview))
- (let ((results)
- (label-sep-len (kview:label-separator-length kview)))
- (if first-p
- ;; Move back to first predecessor at same level.
- (while (kcell-view:backward t label-sep-len)))
- (let ((cell-indent (kcell-view:indent nil label-sep-len)))
- ;; Terminate when no further cells at same level.
- (while (progn (setq results (cons (funcall func kview) results))
- (kcell-view:forward visible-p label-sep-len))))
- (nreverse results))))
-
- (defun kview:map-tree (func kview &optional top-p visible-p)
- "Applies FUNC to the tree starting at point within KVIEW and returns results as a list.
- With optional TOP-P non-nil, maps over all of kview's cells.
- With optional VISIBLE-P, considers only those cells that are visible in the
- view.
-
- FUNC should take one argument, the kview local variable of the current
- buffer or some other kview, and should operate upon the cell at point.
-
- `Cell-indent' contains the indentation value of the first cell mapped when
- FUNC is called so that it may test against this value. `Label-sep-len'
- contains the label separator length.
-
- See also 'kview:map-branch' and 'kview:map-siblings'."
- (let ((results)
- (label-sep-len (kview:label-separator-length kview)))
- (save-excursion
- (set-buffer (kview:buffer kview))
- (if top-p
- (progn (goto-char (point-min))
- (kview:end-of-actual-line)
- ;; Terminate when no further cells to process.
- (while (progn
- (setq results (cons (funcall func kview) results))
- (kcell-view:next visible-p label-sep-len))))
- (let ((cell-indent (kcell-view:indent nil label-sep-len)))
- ;; Terminate when no further cells or when reach a cell at an equal
- ;; or higher level in the kotl than the first cell that we processed.
- (while (and (progn (setq results (cons (funcall func kview) results))
- (kcell-view:next visible-p label-sep-len))
- (> (kcell-view:indent nil label-sep-len)
- cell-indent))))))
- (nreverse results)))
-
- (defun kview:move (from-start from-end to-start from-indent to-indent
- &optional copy-p fill-p)
- "Move tree between FROM-START and FROM-END to TO-START, changing FROM-INDENT to TO-INDENT.
- Copy tree if optional COPY-P is non-nil. Refill cells if optional
- FILL-P is non-nil. Leave point at TO-START."
- (let ((region (buffer-substring from-start from-end))
- (new-start (set-marker (make-marker) to-start))
- collapsed-cells expr new-end space)
- ;;
- ;; Move or copy tree region to new location.
- (or copy-p (delete-region from-start from-end))
- (goto-char new-start)
- (insert region)
- (setq new-end (point))
- ;;
- ;; Change indentation of tree cells.
- (if (/= from-indent to-indent)
- (save-restriction
- (narrow-to-region new-start new-end)
- ;; Store list of which cells are presently collapsed.
- (setq collapsed-cells
- (kview:map-tree
- (function (lambda (view)
- ;; Use free variable label-sep-len bound in
- ;; kview:map-tree for speed.
- (kcell-view:collapsed-p nil label-sep-len)))
- kview t))
- ;; Expand all cells.
- (subst-char-in-region new-start new-end ?\^M ?\n t)
- ;;
- (goto-char (point-min))
- (if (< from-indent to-indent)
- ;; Add indent
- (progn
- (setq expr (make-string (1+ (- to-indent from-indent)) ?\ ))
- (while (re-search-forward "^ " nil t)
- (replace-match expr t t)
- (forward-line 1)))
- ;; Reduce indent in all but first cell lines.
- (setq expr (concat "^" (make-string
- (- from-indent to-indent) ?\ )))
- (while (re-search-forward expr nil t)
- (replace-match "" t t)
- (forward-line 1))
- ;; Reduce indent in first cell lines which may have an
- ;; autonumber or other cell delimiter.
- (setq space (- from-indent to-indent
- (kview:label-separator-length kview)
- 1))
- (if (zerop space)
- nil
- (setq expr (concat "^" (make-string
- (- from-indent to-indent
- (kview:label-separator-length kview)
- 1)
- ?\ )))
- (kview:map-tree
- (function (lambda (view)
- (save-excursion
- (beginning-of-line)
- (if (looking-at expr)
- (replace-match "" t t)))))
- kview t)))
- ;;
- (if fill-p
- ;; Refill cells without no-fill property.
- (kview:map-tree (function (lambda (view)
- (kotl-mode:fill-cell nil t)))
- kview t))
- ;;
- ;; Collapse temporarily expanded cells.
- (if (delq nil collapsed-cells)
- (kview:map-tree
- (function
- (lambda (view)
- (if (car collapsed-cells)
- ;; Use free variable label-sep-len bound in
- ;; kview:map-tree for speed.
- (kcell-view:collapse nil label-sep-len))
- (setq collapsed-cells (cdr collapsed-cells))))
- kview t))))
- ;;
- (goto-char new-start)
- ;;
- ;; Delete temporary markers.
- (set-marker new-start nil)))
-
- (defun kview:set-buffer-name (kview new-name)
- "Set kview's buffer name to NEW-NAME."
- (if (kview:is-p kview)
- (save-excursion
- (let ((buf (kview:buffer kview)))
- (if buf (set-buffer buf)))
- (kview:set-attr kview 'view-buffer-name new-name))
- (error "(kview:set-buffer-name): Invalid kview argument")))
-
- (defun kview:set-label-type (kview new-type)
- "Change kview's label display type to NEW-TYPE, updating all displayed labels.
- See documentation for variable, kview:default-label-type, for
- valid values of NEW-TYPE."
- (interactive (list kview
- (let ((completion-ignore-case)
- (label-type (kview:label-type kview))
- new-type-str)
- (if (string=
- ""
- (setq new-type-str
- (completing-read
- (format "View label type (current = %s): "
- label-type)
- '(("alpha") ("legal") ("id") ("no")
- ("partial-alpha") ("star"))
- nil t)))
- label-type
- (intern new-type-str)))))
- (if (not (memq new-type '(alpha legal id no partial-alpha star)))
- (error "(kview:set-label-type): Invalid label type, '%s'." new-type))
- ;; Disable use of partial-alpha for now since it is broken.
- (if (eq new-type 'partial-alpha)
- (error "(kview:set-label-type): Partial-alpha labels don't work, choose another type"))
- (let ((old-label-type (kview:label-type kview)))
- (if (eq old-label-type new-type)
- nil
- (klabel-type:set-labels new-type)
- (kview:set-attr kview 'label-type new-type)
- (kview:set-functions new-type))))
-
- (defun kview:top-cell (kview)
- "Return kview's invisible top cell with idstamp 0 or nil if argument is not a kview."
- (if (kview:is-p kview)
- (kview:get-attr kview 'top-cell)))
-
- (defun kview:valid-position-p (&optional pos)
- "Return non-nil iff point or optional POS is at a position where editing may occur.
- The read-only positions between cells and within cell indentations are invalid."
- (cond ((null pos)
- (>= (current-column) (kcell-view:indent)))
- ((not (integer-or-marker-p pos))
- (error "(kview:valid-position-p): Argument POS not an integer
- or marker, '%s'" pos))
- ((or (< pos (point-min)) (> pos (point-max)))
- (error "(kview:valid-position-p): Invalid POS argument, '%d'"
- pos))
- (t (save-excursion
- (goto-char pos)
- (>= (current-column) (kcell-view:indent))))))
-
- ;;; ************************************************************************
- ;;; Private functions
- ;;; ************************************************************************
-
- (defun kview:get-attr (obj attribute)
- "Return the value of OBJECT's ATTRIBUTE."
- (car (cdr (memq attribute (car (cdr (memq 'plist obj)))))))
-
- (defun kview:set-attr (obj attribute value)
- "Set OBJECT's ATTRIBUTE to VALUE and return VALUE."
- (let* ((plist-ptr (cdr (memq 'plist obj)))
- (plist (car plist-ptr))
- (attr (memq attribute plist)))
- (if attr
- (setcar (cdr attr) value)
- (setcar plist-ptr
- (nconc (list attribute value) plist)))
- value))
-
- (defun kview:set-functions (label-type)
- "Setup functions which handle labels of LABEL-TYPE for current view."
- (kview:set-attr kview 'label-function (klabel-type:function label-type))
- (kview:set-attr kview 'label-child (klabel-type:child label-type))
- (kview:set-attr kview 'label-increment (klabel-type:increment label-type))
- (kview:set-attr kview 'label-parent (klabel-type:parent label-type))
- (kview:set-attr kview 'to-label-end (klabel-type:to-label-end label-type)))
-
- (defun kview:set-label-separator (kview label-separator)
- "Set within KVIEW the LABEL-SEPARATOR (a string) between labels and cell contents."
- (kview:set-attr kview 'label-separator label-separator)
- (kview:set-attr kview 'label-separator-length (length label-separator)))
-
- (provide 'kview)
-