; -*-Emacs-Lisp-*- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; File: hyper.el ; Description: Hypertext support using EPOCH zones.... ; Author: Dennis F. Freeze ; Created: Fri Jun 22 13:21:21 1990 ; Modified: Fri Jun 19 17:52:31 1992 (Dennis F. Freeze) dff@hpldff ; Language: Emacs-Lisp ; Status: Experimental (Do Not Distribute) ; ; (c) Copyright 1990, Hewlett-Packard Company ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (provide 'hyper) (require 'hyper-rep) (require 'hyper-server) (require 'hyper-aux) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; THINGS TO DO.... ;; ;; 1. Settle the section-name issue. It appears that the name was only being ;; used to type the sections, and the section-object was storing the link's ;; UUID. Proposal: don't make zones for the text stuff -- only for links. ;; This way, you don't need to differentiate between them (unless there ;; are other kinds of zones in a buffer), so it's OK to just store the ;; link-id in the data field. To allow for other kinds of zones in a ;; buffer, it's probably best to structure the data field to hold both ;; a type *and* a link-id. ;; ;; PROPOSAL: since section-name is no longer avail, use the zone-data ;; field for everything. The zone-data for a link cue will have this form: ;; ;; ("*KIOSK*" "LINK-ID") ;; ;; Text areas will *not* have zones, as they did in the Sections version. ;; It will be necessary to check to make sure a zone is really a link cue, ;; since there can be other kinds of zones. ;; ;; 2. Write functions to allow for previous/next zone traversals. ;; ;; 3. Conversion process: ;; - Rework link registry. ;; - Create a link. ;; - Display link cues. ;; - Interactively create a link [coerce to reasonable locn, too]. ;; - Traverse, delete a link. ;; - Split a buffer into text and links. ;; - Sync with Kiosk. ;; - Use change functions to track changes in text, links. ;; - Modify an existing link. ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Global variables.... ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; User Customization Variables.... ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (setq debug-on-error t) ;; annoying to have ON all the time! (defvar *verbose-sections* t) ;; Echo things as they happen? (defvar *visible-section-log* nil) ;; Make sure the log is visible? (defvar *prompt-for-role* nil) ;; prompt for role or take default? (defvar *default-link-role* "Comment") (defvar *default-link-owner* (user-login-name)) ;; Appearance of text / link-cues: (defvar *verbose-link-cues* t) ;; Show link cues verbosely? (defvar *text-color* "White") ;; Set color for normal text sections (defvar *source-font* "hp8.8x16b") (defvar *source-link-color* "Salmon") ;; Set color for 'Source' link-cues (defvar *dest-font* "hp8.8x16b") (defvar *dest-link-color* "Magenta") ;; Set color for 'Dest' link-cues (defvar *value-font* "hp8.8x16b") (defvar *value-link-color* "Thistle") ;; Set color for 'Value_link' cues ;; Make the style objects to use for the different kinds of zones: (defvar *source-style* (make-style)) (set-style-foreground *source-style* *source-link-color*) (set-style-font *source-style* *source-font*) (set-style-underline *source-style* *source-link-color*) (defvar *dest-style* (make-style)) (set-style-foreground *dest-style* *dest-link-color*) (set-style-font *dest-style* *dest-font*) (set-style-underline *dest-style* *dest-link-color*) (defvar *value-style* (make-style)) (set-style-font *value-style* *value-font*) (set-style-foreground *value-style* *value-link-color*) (set-style-underline *value-style* *value-link-color*) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Internal Variables -- Do Not Change !!! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar *selected-link* nil) ;; for moving link-endpoints ;; For recording whether or not a buffer has had the initial sections set up: (make-variable-buffer-local '*zones-initialized*) (setq-default *zones-initialized* nil) ;; have we set up the section yet? ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Initialization stuff: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun initialize-hyper-kiosk() ;; Fix up all of the initial sections in non-special buffers, in case we ;; want to make links in them later: (kiosk-fixup-initial-buffers) ;; Set up the hooks that control things: (setup-kiosk-hooks) ;; Set up some scratch buffers for parsing things: (initialize-parse-buffers) ;; Initialize some things for making links: (initialize-link-creation) ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Creating links -- Interactive version.... ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Basic model for interactive link creation: You must finish one link before ;; creating another. The first time CREATE-LINK is invoked, it inits things ;; and prompts for the new link's role. The next 2 times it is called, the ;; (current-buffer) and (point) are used as the Source and Dest of the new ;; link, respectively. If the flag *prompt-for-role* is nil, however, the ;; user is not prompted for the role -- *default-link-role* is used instead, ;; and the first call proceeds with capturing the source endpoint. ;; ;; *pending-link* is a link-info vector with certain pieces being filled in ;; interactively. The pieces for which the user is prompted are: ;; (src-endpnt {file, offset}; dest-endpnt {file, offset}; role. ;; (defvar *pending-link* (generate-link-object (list (list 'type 'Link) (list 'status 'live) ))) (defvar *pending-link-state* nil) ;; { nil (role) / source / dest } (defvar *pending-link-id* nil) ;; unique id for the new link (defun reset-pending-link () ;; Create a new link-info vector for building a link interactively. ;; Reset some globals (aren't they pretty?): (setq *pending-link-state* nil) (setq *pending-link-id* nil) ;; Create and return the link-info-vector: (setq *pending-link* (generate-link-object (list (list 'type 'Link) (list 'status 'live) ))) ) (defun create-link () ;; This function either initiates creation of a new link, or continues ;; the creation by setting endpoints of the link.... (interactive) (cond ((null *pending-link-state*) (setq *pending-link-id* (generate-link-id)) (cond (*prompt-for-role* (call-interactively 'initiate-create-link) ) (t ;; Skip the prompting -- take the default and run with it: (set-role *pending-link* *default-link-role*) (setq *pending-link-state* 'source) (message (format "Link %s will have role '%s'." *pending-link-id* *default-link-role*)) (when *verbose-sections* (log-message (format "Link %s will have role '%s'.\n" *pending-link-id* *default-link-role*)) ) ;; Since we're not prompting, go ahead and proceed with ;; endpoint captures: (set-link-endpoint) )) ) ((eq *pending-link-state* 'source) (set-link-endpoint) ) ((eq *pending-link-state* 'dest) (set-link-endpoint) (complete-link-creation) ) (t (message "ERROR: Invalid *pending-link-state* encountered!") )) ) (defun initiate-create-link (prompt-role) (interactive "sEnter the link's ROLE or : ") (let ((role (if (equal prompt-role "") *default-link-role* prompt-role )) ) (set-role *pending-link* role) (setq *pending-link-state* 'source) (message "Link %s will have role '%s'." *pending-link-id* role) (when *verbose-sections* (log-message (format "Link %s will have role '%s'.\n" *pending-link-id* role)) ) )) (defun set-link-endpoint () (let* ((buffer *kiosk-node-name*) (posn (point)) (current-section (epoch::zone-at posn)) (kiosk-zone (kiosk-zone-p current-section)) ) ;; First, either adjust the position (if over a link-cue), or split the ;; section (if over a text section).... (cond ((equal name "*link*") ;; We're over a link-cue, so move to the end of it to insert ;; the new one: (setq current-section (goto-next-section)) (if current-section (setq posn (epoch::zone-start current-section)) (setq posn (point-max)) ) )) (cond ((equal *pending-link-state* 'source) (set-abs-src *pending-link* buffer) (set-src-offset *pending-link* posn) (setq *pending-link-state* 'dest) (message (format "Link %s: Source is %s" *pending-link-id* (list buffer posn) )) (when *verbose-sections* (log-message (format "Link %s: Source is %s\n" *pending-link-id* (list buffer posn) )) ) ) ((equal *pending-link-state* 'dest) (set-abs-dest *pending-link* buffer) (set-dest-offset *pending-link* posn) (message (format "Link %s: Dest is %s" *pending-link-id* (list buffer posn))) (when *verbose-sections* (log-message (format "Link %s: Dest is %s\n" *pending-link-id* (list buffer posn))) ) ) (t (message "ERROR: Unknown state in SET-LINK-ENDPOINT!") (log-message "ERROR: Unknown state in SET-LINK-ENDPOINT!\n") )) )) (defun complete-link-creation () ;; All necessary info has been gathered, so actually create the link: (let ( (src-buffer (get-abs-src *pending-link*)) (src-posn (get-src-offset *pending-link*)) (dest-buffer (get-abs-dest *pending-link*)) (dest-posn (get-dest-offset *pending-link*)) (src-section nil) (dest-section nil) ) ;; Flesh out the *pending-link* link-info vector: (set-owner *pending-link* *default-link-owner*) (set-rel-src *pending-link* (compress-filename src-buffer)) (set-rel-dest *pending-link* (compress-filename dest-buffer)) ;; Register the link with info about the endpoints: (put-link-attributes *pending-link-id* *pending-link*) ;;(log-message (format "!!! CLC : %s: attr = %s\n" ;; *pending-link-id* *pending-link*)) ;; Now, create the link-cues: (cond ((equal src-buffer dest-buffer) ;; If both in same buffer, make sure that insertion of cue text ;; doesn't mess up the offset for the other link cue. (save-excursion ;;(log-message (format "=== CLC0: %s\n" src-buffer)) (set-buffer (find-or-create-buffer src-buffer)) (cond ((< src-posn dest-posn) (goto-char dest-posn) (setq dest-section (create-link-cue *pending-link-id* *pending-link* 'dest)) (goto-char src-posn) (setq src-section (create-link-cue *pending-link-id* *pending-link* 'source)) ) (t ;; else (goto-char src-posn) (setq src-section (create-link-cue *pending-link-id* *pending-link* 'source)) (goto-char dest-posn) (setq dest-section (create-link-cue *pending-link-id* *pending-link* 'dest)) ) ) ) ) (t ;; The endpoints are in different buffers, so just create them. ;; Do the Source endpoint first: (save-excursion ;;(log-message (format "=== CLC1: %s\n" src-buffer)) (set-buffer (find-or-create-buffer src-buffer)) (goto-char src-posn) (setq src-section (create-link-cue *pending-link-id* *pending-link* 'source)) (recenter) ) ;; Now do the Dest endpoint: (save-excursion ;;(log-message (format "=== CLC2: %s attr='%s'\n" ;; dest-buffer ;; *pending-link* ;; )) (set-buffer (find-or-create-buffer dest-buffer)) (goto-char dest-posn) (setq dest-section (create-link-cue *pending-link-id* *pending-link* 'dest)) (recenter) ) )) ;; Finally, register the link endpoints: (set-src-section *pending-link* src-section) (set-dest-section *pending-link* dest-section) (when *verbose-sections* (log-message (format "Link '%s' created: Role = '%s' Src = %s Dest = %s\n" *pending-link-id* (get-role *pending-link*) (or (get-rel-src *pending-link*) (get-abs-src *pending-link*) ) (or (get-rel-dest *pending-link*) (get-abs-dest *pending-link*) ) ))) ;; Clean up by resetting the *pending* vars: (reset-pending-link) )) (defun create-link-cue (link-id link-attr direction) ;; Create a new section at point representing the link....By the time ;; it reaches here, the buffer should already have been created, even if ;; it wasn't read in when this link creation occurred. (let* ((buffer (current-buffer)) (cue-posn (point)) (current-section (epoch::zone-at cue-posn)) (section-posn (epoch::zone-start current-section)) (name (section-name current-section)) ;; (string (return-link-cue-text link-id link-attr direction)) (return-result nil) (continue-creation nil) link-section fn ) ;;(log-message ;; (format "CLC: %s (%s) from %s\n" link-id direction link-attr)) ;; ;; First, either adjust the position (if over a link-cue), or split the ;; section (if over a text section).... (cond ((equal name "*link*") ;; We're over a link-cue, so move to the end of it to insert ;; the new one: (setq current-section (goto-next-section)) (if current-section (setq cue-posn (epoch::zone-start current-section)) (setq cue-posn (point-max)) ) (setq continue-creation t) ) (t ;; Split the existing text section at point. Don't split it ;; if at the start of the section, since it's already been split ;; by some other operation. (if (equal cue-posn section-posn) (setq continue-creation t) (setq continue-creation (split-text-section)) ) )) ;; Next, make a new section and insert it: (cond (continue-creation (setq link-section (make-zone)) (set-section-name link-section "*link*") (setq current-section (epoch::zone-at cue-posn)) ;;(log-message (format "... CUE1: curr='%s' link='%s'\n" ;; current-section link-section)) (when (and current-section (epoch::zone-read-only current-section) ) (unlock-zone current-section) ) ;; Insert the actual link-cue text: (insert-before-markers string) ;; Finally, set up the link-cue section: (goto-char cue-posn) (set-section link-section cue-posn buffer) (if (equal direction 'source) (epoch::set-zone-style link-section *source-style*) (epoch::set-zone-style link-section *dest-style*) ) ;; (epoch::set-zone-read-only link-section t) ;; (when (and (epoch::zone-at (point)) (epoch::zone-read-only (epoch::zone-at (point))) ) (unlock-zone current-section) ) ;; Register it: (set-section-object link-section link-id) ;; Return the section object: (setq return-result link-section) ) (t (message "WARNING: Unable to create the requested link cue!") (when *verbose-sections* (log-message "WARNING: Unable to create the requested link cue!\n") ) )) ;; Return the resulting section or nil: return-result )) (defun return-link-cue-text (link-id link-attr direction) (let ((cue-text "") (role (get-role link-attr)) ) ;; Should this show the LINK-ID also? (cond (*verbose-link-cues* (if (equal direction 'source) (setq cue-text (format "[%s]" role)) (setq cue-text (format "[%s]" role)) ) ) (t (if (equal direction 'source) (setq cue-text "^") (setq cue-text "v") ) )) cue-text )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Interactively create a Value_link: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun create-value-link (role value) (interactive "sEnter the Value_link's ROLE: \nsEnter the VALUE: ") ;; This function creates a new Value link at point by prompting for the ;; needed info: (cond ((and (not (equal role "")) (not (equal value "")) ) (let ((link-attr-vec (generate-link-object (list (list 'type 'Value_link) (list 'owner *default-link-owner*) (list 'offset (point)) (list 'role role) (list 'value value) ))) ) (create-value-link-anchor link-attr-vec) (register-value-link link-attr-vec) )) (t (message "ERROR: A Value_link must have Role and Value set!") )) ) (defun register-value-link (link-attr-vec) ;; Register a new value link in the registry: (put-link-attributes link-id link-attr-vec) ) (defun create-value-link-anchors (link-attr-vec) ;; ;; Create the link-cue for a Value_link: ;; (let* ( (link-id (get-id link-attr-vec)) (extant-link (and link-id (link-exists link-id))) (src-section nil) ;; (owner (get-owner link-attr-vec)) (src-posn (get-offset link-attr-vec)) (role (get-role link-attr-vec)) (value (get-value link-attr-vec)) ;; (rel-posn nil) (buffer nil) (current-buffer (current-buffer)) link-status ) ;; Find or generate a link-id for the new link: (cond (extant-link ;; We're done, since no other endpoint for this type: (setq link-status (get-status link-attr-vec)) (when (eq link-status 'orphan) ;; Fix the status: (set-status link-attr-vec 'live) ;; The link-cue was previously removed, so replace it: (save-excursion (setq rel-posn src-posn) ;; *** ??? What's the deal??? *** ;;(setq rel-posn (determine-relative-offset src-posn)) (goto-char rel-posn) (setq src-section (create-value-link-cue link-id link-attr-vec)) ) ;; Register the new section: (set-value-section link-attr-vec src-section) ;;(put-link-attributes link-id link-attr-vec) (when *verbose-sections* (log-message (format "Value_Link '%s' restored: Role='%s' Value='%s' Owner='%s'\n" link-id role value owner ))) ) ) (t ;; It's a new link, so generate a new link-id: (unless link-id (setq link-id (generate-link-id)) (set-id link-attr-vec link-id) ) ;; Create the link-cue: (save-excursion (setq rel-posn src-posn) ;; *** ??? What's the deal??? *** ;;(setq rel-posn (determine-relative-offset src-posn)) (goto-char rel-posn) (setq src-section (create-value-link-cue link-id link-attr-vec)) ) ;; Finally, register the link with info about the endpoint: (set-value-section link-attr-vec src-section) (when *verbose-sections* (log-message (format "Value_Link '%s' created: Role='%s' Value='%s' Owner='%s'\n" link-id role value owner ))) )) )) (defun create-value-link-cue (link-id link-attr) ;; Create a new section at point representing the link....By the time ;; it reaches here, the buffer should already have been created, even if ;; it wasn't read in when this link creation occurred. (let* ((buffer (current-buffer)) (cue-posn (point)) (current-section (epoch::zone-at cue-posn)) (section-posn (epoch::zone-start current-section)) (name (section-name current-section)) ;; (role (get-role link-attr)) (value (get-value link-attr)) (string (return-value-link-cue-text link-id link-attr)) (return-result nil) (continue-creation nil) link-section fn ) ;; First, either adjust the position (if over a link-cue), or split the ;; section (if over a text section).... (cond ((equal name "*link*") ;; We're over a link-cue, so move to the end of it to insert ;; the new one: (setq current-section (goto-next-section)) (if current-section (setq cue-posn (epoch::zone-start current-section)) (setq cue-posn (point-max)) ) (setq continue-creation t) ) (t ;; Split the existing text section at point. Don't split it ;; if at the start of the section, since it's already been split ;; by some other operation. (if (equal cue-posn section-posn) (setq continue-creation t) (setq continue-creation (split-text-section)) ) )) ;; Next, make a new section and insert it: (cond (continue-creation (setq link-section (make-section)) (set-section-name link-section "*link*") (epoch::set-zone-style link-section *value-style*) ;; Insert the actual link-cue text: (setq current-section (epoch::zone-at cue-posn)) (when (and current-section (epoch::zone-read-only current-section) ) (unlock-zone current-section) ) (goto-char cue-posn) ;;(log-message (format " ... inserting vlc at %d\n" cue-posn)) (insert-before-markers string) ;; Finally, set up the link-cue section: (goto-char cue-posn) (set-section link-section cue-posn buffer) ;; (epoch::set-zone-read-only link-section t) ;; ;(when (and current-section ; (epoch::zone-read-only current-section) ; ) ; (lock-zone current-section) ; ) ;; Register it: (set-section-object link-section link-id) ;; Return the section object: (setq return-result link-section) ) (t (message "WARNING: Unable to create the requested link cue!") (when *verbose-sections* (log-message "WARNING: Unable to create the requested link cue!\n") ) )) ;; Return the resulting section or nil: return-result )) (defun return-value-link-cue-text (link-id link-attr) ;; Link-attr contains all known attributes of the link -- Use the ;; 'get-xxx' macros defined in hyper-rep.el to reference fields. (let ((cue-text "") (role (get-role link-attr)) (value (get-value link-attr)) ) (cond (*verbose-link-cues* (setq cue-text (format "[%s:%s]" role value)) ) (t (setq cue-text "$") )) cue-text )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Link Operations -- interactive.... ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun traverse-link () ;; If (point) in (current-buffer) is over a link-cue, then traverse it ;; to the other endpoint, wherever that might be.... (interactive) (let* ((curr-buffer (current-buffer)) (posn (point)) (current-section (epoch::zone-at posn)) (name (section-name current-section)) ) (cond ((equal name "*link*") ;; We're over a link-cue, so traverse it (unless a Value-link): (let* ((link-id (get-section-object current-section)) (link-attr (get-link-attributes link-id)) (link-type (when link-attr (get-type link-attr))) (link-status (when link-attr (get-status link-attr))) ) ;; Figure out which endpoint we were over, and move to the other: (cond ((not (eq link-status 'live)) (message "Link %s has weird status: %s" link-id link-attr) (when *verbose-sections* (log-message (format "Link %s has weird status: %s\n" link-id link-attr)) ) ) ((eq link-type 'Value_link) (message "Can't traverse a Value_Link!") ) (t (let ((src-pn (get-abs-src link-attr)) (src-sec (get-src-section link-attr)) (src-off (get-src-offset link-attr)) (dest-pn (get-abs-dest link-attr)) (dest-sec (get-dest-section link-attr)) (dest-off (get-dest-offset link-attr)) buffer section ) (cond ((eq current-section src-sec) ;; Over the Source, so go to the Dest: (when *verbose-sections* (log-message (format "Traverse %s : from Src = %s (%s) to Dest = %s (%s)\n" link-id src-pn (if src-sec t nil) dest-pn (if dest-sec t nil) )) ) (setq buffer (get-node-buffer dest-pn)) (cond (buffer (pop-to-buffer buffer) (set-buffer buffer) (goto-char (epoch::zone-start dest-sec)) ) (t ;; Didn't find it, so request Kiosk to browse it: (kiosk-browse-node dest-pn dest-off) )) ) ((eq current-section dest-sec) ;; Over the Dest, so go to the Source: (when *verbose-sections* (log-message (format "Traverse %s : from Dest = %s (%s) to Src = %s (%s)\n" link-id dest-pn (if dest-sec t nil) src-pn (if src-sec t nil) )) ) (setq buffer (get-node-buffer src-pn)) (cond (buffer (pop-to-buffer buffer) (set-buffer buffer) (goto-char (epoch::zone-start src-sec)) ) (t ;; Didn't find it, so request Kiosk to browse it: (kiosk-browse-node src-pn src-off) )) ) (t (when *verbose-sections* (log-message "\n**** ERROR in TRAVERSE ****\n") ) )) ) )) )) ((or (equal name "*text*") (null name) ) ;; Not over a link -- what were they doing? (message "WARNING: Unable to traverse a non-link-cue!") (when *verbose-sections* (log-message "WARNING: Attempted traversal not over link-cue!\n") ) )) )) (defun user-delete-link () (interactive) ;; Delete the link at (point) if any. (let* ((buffer (current-buffer)) (posn (point)) (current-section (epoch::zone-at posn)) (name (section-name current-section)) ) (cond ((equal name "*link*") ;; We're over a link-cue, so delete it: (let* ((link-id (get-section-object current-section)) (link-attr (get-link-attributes link-id)) (link-type (get-type link-attr)) buffer section len ) (message (format "Deleting Link %s : Attributes = %s\n" link-id link-attr)) (when *verbose-sections* (log-message (format "Deleting Link %s : Attributes = %s\n" link-id link-attr)) ) ;; See what kind of Link it is: (cond ((eq link-type 'Value_link) (delete-link-cue current-section) ) ((eq link-type 'Link) ;; Delete the Source: (setq section (get-src-section link-attr)) (delete-link-cue section) ;; Delete the Dest: (setq section (get-dest-section link-attr)) (delete-link-cue section) )) ;; Remove from registry: (set-status link-attr 'dead) ;;(log-message ;; (format "!!! DELETING %s : %s\n" link-id link-attr)) ) ) (t ;; Not over a link.... (message "WARNING: Delete-Link occurred over pure text.") (when *verbose-sections* (log-message "WARNING: Delete-Link occurred over pure text.\n") ) )) )) (defun delete-link-cue (section) ;; Given the section representing the link-cue, delete it from its buffer. ;; This only deals with the link-cue, not the link itself. It does, ;; however, update the link-registry to remove pointers to the section. (interactive) (let* ((buffer (epoch::zone-buffer section)) (name (section-name section)) link-id link-info link-type len prev-section (prev-locked nil) ) (save-excursion (set-buffer buffer) ;; (cond ((and (equal name "*link*") (setq link-id (get-section-object section)) (setq link-info (get-link-attributes link-id)) ) ;; We're over a link-cue, so delete it: (setq link-type (get-type link-info)) (message "Deleting Link Cue %s" section) (when *verbose-sections* (log-message (format "Deleting Link Cue %s\n" section)) ) ;; Update the registry to remove the pntr to the section: (cond ((eq link-type 'Value_link) (set-value-section link-info nil) ) ((eq link-type 'Link) (if (eq section (get-src-section link-info)) (set-src-section link-info nil) (set-dest-section link-info nil) ) )) ;; Actually delete the section object and the cue-text: (save-excursion (unlock-zone section) (goto-char (epoch::zone-start section)) (setq len (length (zone-text section))) (delete-section-object section) (epoch::delete-zone section) ;; Must unlock if the section it collapsed into is READ-ONLY: (setq prev-section (epoch::zone-at (point))) (setq prev-locked (epoch::zone-read-only prev-section)) (when prev-locked (unlock-zone prev-section) ) (delete-char len) (when prev-locked (lock-zone prev-section) ) ) ) (t ;; Not over a link.... (when *verbose-sections* (log-message (format "WARNING: Delete-Link-Cue called on non-link section %s.\n" section )) ) )) ) ;; outer save-excursion.... )) (defun describe-link () ;; If (point) in (current-buffer) is over a link-cue, then describe it: (interactive) (let* ((buffer (current-buffer)) (posn (point)) (current-section (epoch::zone-at posn)) (name (section-name current-section)) ) (cond ((equal name "*link*") ;; We're over a link-cue, so describe it: (let* ((link-id (get-section-object current-section)) (link-attr (get-link-attributes link-id)) (link-type (get-type link-attr)) ) (cond ((eq link-type 'Value_link) (let ((role (get-role link-attr)) (value (get-value link-attr)) (section (get-value-section link-attr)) offset ) (setq offset (- (determine-pure-offset section) 1)) (when *verbose-sections* (log-message (format "Describe Value_link %s : Role = '%s' Value = '%s' Offset = %d\n" link-id role value offset )) ) (message (format "Value_link %s : Role = '%s' Value = '%s' Offset = %d" link-id role value offset )) ) ) (t ;; It's a regular link: (let* ( (rel-src (get-rel-src link-attr)) (src-pn (get-abs-src link-attr)) (src-sec (get-src-section link-attr)) (rel-dest (get-rel-dest link-attr)) (dest-pn (get-abs-dest link-attr)) (dest-sec (get-dest-section link-attr)) ) ;; Figure out which endpoint we were over: (cond ((eq current-section src-sec) ;; Over the Source, so go to the Dest: (when *verbose-sections* (log-message (format "Describe %s : *Src = %s (%s) Dest = %s (%s)\n" link-id (or rel-src src-pn) (if src-sec t nil) (or rel-dest dest-pn) (if dest-sec t nil) )) ) (message (format "%s : *S (%s) ='%s' D (%s) = '%s'" link-id (if src-sec t nil) (or rel-src src-pn) (if dest-sec t nil) (or rel-dest dest-pn) )) ) (t ;; Over the Dest, so go to the Source: (when *verbose-sections* (log-message (format "Describe %s : Src = %s (%s) *Dest = %s (%s)\n" link-id (or rel-src src-pn) (if src-sec t nil) (or rel-dest dest-pn) (if dest-sec t nil) )) ) (message (format "%s : S (%s) = '%s' *D (%s) = '%s'" link-id (if src-sec t nil) (or rel-src src-pn) (if dest-sec t nil) (or rel-dest dest-pn) )) )) )) ))) (t ;; Not over a link.... (message "WARNING: Describe-Link occurred over pure text.") (when *verbose-sections* (log-message "WARNING: Describe-Link occurred over pure text.\n") ) )) )) (defun previous-link () ;; Jump to the next link found before (point). (interactive) (let ((section (epoch::zone-at)) (link-section nil) (continue-flag t) (full-circle nil) name ) (save-excursion (while continue-flag (setq section (goto-previous-section)) (cond (section (setq name (section-name section)) (setq full-circle nil) ) (t ;; else see if we've come full-circle: (cond (full-circle (setq continue-flag nil) ) (t (setq full-circle t) (goto-char (point-max)) (setq section (epoch::zone-at)) (setq name (section-name section)) )) )) (when (equal name "*link*") (setq link-section section) (setq continue-flag nil) ) )) ;; If we found it, go there: (when link-section (goto-section link-section) (describe-link) ) )) (defun next-link () ;; Jump to the next link found after (point). (interactive) (let ((section (epoch::zone-at)) name (link-section nil) (continue-flag t) (full-circle nil) ) (save-excursion (while continue-flag (setq section (goto-next-section)) (cond (section (setq name (section-name section)) (setq full-circle nil) ) (t ;; else see if we've come full-circle: (cond (full-circle (setq continue-flag nil) ) (t (setq full-circle t) (goto-char (point-min)) (setq section (epoch::zone-at)) (setq name (section-name section)) )) )) (when (equal name "*link*") (setq link-section section) (setq continue-flag nil) ) )) ;; If we found it, go there: (when link-section (goto-section link-section) (describe-link) ) )) (defun move-link-endpoint () ;; Either select an endpoint to be moved, or tell where the selected ;; link should go: ;; *** Make sure to splice text-sections together when appropriate! *** (interactive) (let* ((posn (point)) (section (epoch::zone-at posn)) (sec-type (section-name section)) string sel-posn adj-posn sel-section new-section link-id link-attr link-type dir ) (cond (*selected-link* ;;(message "Move %s to %d." *selected-link* posn) ;;(log-message (format "Move %s to %d." *selected-link* posn)) ;; *** Actually move it! *** (save-excursion (setq link-id (get-section-object *selected-link*)) (setq link-attr (get-link-attributes link-id)) (setq link-type (get-type link-attr)) ;;(log-message (format "MOVE: link-id = %s link-attr = %s\n" ;; link-id link-attr)) ;; (goto-char (epoch::zone-start *selected-link*)) (setq sel-posn (point)) ;;(log-message (format "MOVE: Section = %s Point = %d\n" ;; *selected-link* (point))) (setq string (zone-text *selected-link*)) (setq len (length string)) (unlock-zone *selected-link*) (delete-section-object *selected-link*) (epoch::delete-zone *selected-link*) ;;(log-message (format "Destroyed SEL = %s\n" *selected-link*)) (unlock-zone (epoch::zone-at sel-posn)) (delete-char len) ;; Now, add it back at the new place: ;;(log-message (format "Insert-pnt = %d\n" posn)) (when (> posn sel-posn) (setq posn (- posn (length string))) ) ;;(log-message (format "Insert-pnt adjusted to %d\n" posn)) (goto-char posn) (cond ((equal link-type 'Link) (setq dir (if (equal *selected-link* (get-src-section link-attr)) 'source 'dest )) (unlock-zone (epoch::zone-at posn)) (setq new-section (create-link-cue link-id link-attr dir)) (set-section-object new-section link-id) (if (equal dir 'source) (set-src-section link-attr new-section) (set-dest-section link-attr new-section) ) ) ((equal link-type 'Value_link) (setq dir 'value) (unlock-zone (epoch::zone-at posn)) (setq new-section (create-value-link-cue link-id link-attr)) (set-section-object new-section link-id) (set-value-section link-attr new-section) )) ;; ;;(split-text-section) ;;(unlock-zone (epoch::zone-at posn)) ;; ;; **************************************** ;; * Fix this! Make a new section using create-link-cue!!! ;; **************************************** ;(log-message (format "> Inserting at %d: '%s'\n" ; posn string)) ;(log-message (format "> BEFORE: (point) = %d POSN = %d\n" ; (point) posn)) ;(insert-before-markers string) ;(log-message (format "> AFTER : (point) = %d POSN = %d\n" ; (point) posn)) ;;(setq posn (- posn (length string))) ;;(when (> posn sel-posn) ;; (setq posn (- posn (length string))) ;; ) ;(log-message (format "> ADJUSTED: (point) = %d POSN = %d\n" ; (point) posn)) ;(log-message (format "> Section-start at %d for %s\n" ; posn *selected-link*)) ;(set-section *selected-link* posn (current-buffer)) ;(log-message (format "Reset SEL = %s String = '%s'\n" ; *selected-link* ; (zone-text *selected-link*))) ) ;; Finally, reset this: (setq *selected-link* nil) ) (t (cond ((equal sec-type "*text*") (when *selected-link* (message "Selected-link is now reset to NIL.") (setq *selected-link* nil) ) ) ((equal sec-type "*link*") (setq *selected-link* section) (message "Selected-link is now %s." section) )) )) )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Mouse i/f to link creation.... ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun x-mouse-capture-link-endpoint (arg) ;; When this is bound to a mouse button (RightButton by default), this ;; invokes the CreateLink function, which allows you to interactively ;; create a link by nothing more than 2 mouse-clicks. (let ((*prompt-for-role* nil) ) (x-mouse-set-point arg) (call-interactively 'create-link) )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Killing all existing links.... ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun kill-all-links () ;; Get rid of all links from the registry and buffers.... (interactive) ) (defun delete-link (link-id) ;; Get rid of a link from the registry and buffers.... (interactive) (let* ( (link-info (get-link-attributes link-id)) link-type section ) (unless (or (null link-info) (eq (get-status link-info) 'dead) ) (setq link-type (get-type link-info)) ;; See what kind of Link it is: (cond ((eq link-type 'Value_link) (setq section (get-value-section link-info)) (when section (delete-link-cue section) ) ) ((eq link-type 'Link) ;; Delete the Source: (setq section (get-src-section link-info)) (when section (delete-link-cue section) ) ;; Delete the Dest: (setq section (get-dest-section link-info)) (when section (delete-link-cue section) ) )) ;; Remove from registry: (delete-link-attributes link-id) (when *verbose-sections* (log-message (format "!!! DELETING %s : %s\n" link-id link-info)) ) ) )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun determine-pure-offset (target-section) ;; Take the section's position and modify it, based on whether or not any ;; link-cues would push it back.... (save-excursion (set-buffer (epoch::zone-buffer target-section)) (goto-char (point-min)) (let ((actual-offset (epoch::zone-start target-section)) section-posn section-len section-type (continue t) (section (epoch::zone-at)) ) (while continue (setq section-posn (epoch::zone-start section)) (setq section-type (section-name section)) (cond ((eq section target-section) (setq continue nil) ) ((equal section-type "*link*") ;; Over a link-cue, so adjust the offset to compensate: (setq section-len (length (zone-text section))) (setq actual-offset (- actual-offset section-len)) )) (setq section (goto-next-section)) ;; Make sure we're not off the end of the world: (unless section (setq continue nil) ) ) ;; Return the adjusted offset: actual-offset ) )) (defun determine-relative-offset (pure-offset) ;; Take the pure (text-only) offset and modify it, based on whether or not ;; any link-cues would push it back.... (save-excursion (goto-char (point-min)) (let* ((actual-offset pure-offset) section-posn section-len section-type (continue t) (section (epoch::zone-at)) ) (while continue (setq section-posn (epoch::zone-start section)) (setq section-len (length (zone-text section))) (setq section-type (section-name section)) ;; (cond ((equal section-type "*link*") ;; Over a link-cue, so just adjust the offset to compensate: (setq actual-offset (+ actual-offset section-len)) ) (t ;; It's a text section -- see if we have found it: (cond ((and (<= section-posn actual-offset) (< actual-offset (+ section-posn section-len)) ) ;; It's in this section, so stop looking: (setq continue nil) ) (t ;; Not in this section, but no need to adjust it. )) )) (setq section (goto-next-section)) ;; Make sure we're not off the end of the world: (unless section (setq continue nil) ) ) ;; Return the adjusted offset: actual-offset ) )) (defun links-in-buffer (pn) ;; Return whether or not there are links in the buffer for PN.... (let ((continue-flag t) (buffer (find-or-create-buffer pn)) section (links-found nil) section-name ) (save-excursion (set-buffer buffer) (setq section (epoch::zone-at (point-min))) (while continue-flag (setq section-name (section-name section)) (cond ((equal section-name "*link*") (setq continue-flag nil) (setq links-found t) ) (t (setq section (goto-next-section)) (unless section (setq continue-flag nil) ) )) )) ;; Return the flag: links-found )) (defun kiosk-buffer-name (buffer) ;;(save-excursion (set-buffer buffer) *kiosk-node-name*) ;; Use an EPOCH nicety.... (symbol-buffer-value '*kiosk-node-name* buffer) ) (defun kiosk-zone (posn) ;; Return the *kiosk* zone at posn if any, else return nil.... (let ((zone (epoch::zone-at posn)) ) (if (kiosk-zone-p zone) zone ) )) (defun kiosk-zone-p (zone) ;; Return whether or not the given zone is a *kiosk* zone.... (let (data) (cond ((zonep zone) (setq data (epoch::zone-data zone)) (and data (listp data) (equal (car data) "*kiosk*") ) )) )) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ***************************************************************************** ;; *** WARNING! The following function will modify your GNU. Make no mistake ;; *** about it! The first form just adds a find-file-hook, but ;; *** the second actually replaces the save-buffer function, saving ;; *** the old function as 'original-save-buffer. The third form ;; *** causes REVERT-BUFFER to use a Kiosk-specific func. ;; ***************************************************************************** ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun setup-kiosk-hooks () ;; ;; This find-file-hook invokes the automatic reading of link-files whenever ;; a file is read into a buffer (can be disabled with *autoread-link-files*), ;; or when a buffer is reverted. ;; ;;(unless (memq 'read-link-file-hook find-file-hooks) ;; (push 'read-link-file-hook find-file-hooks) ;; ) ;; ;; This replaces the original save-buffer function with my own version, ;; but it saves the old definition and uses it when there are no links. ;; ;;(unless (fboundp 'original-save-buffer) ;; (fset 'original-save-buffer (symbol-function 'save-buffer)) ;; (fset 'save-buffer (symbol-function 'kiosk-save-current-file)) ;; ) ;; ;; This causes revert-buffer to use the designated function to do its work: ;; ;;(unless revert-buffer-function ;; (setq revert-buffer-function 'kiosk-revert-buffer) ;; ) ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; User-settable options.... ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun set-default-role (new-role) ;; This sets the default link role, used when the user does not want to be ;; prompted for the role: (interactive "sEnter new link role default: ") (cond ((not (equal new-role "")) (setq *default-link-role* new-role) (message (format "Default-Link-Role is now '%s'." new-role)) )) ) (defun toggle-role-prompt () ;; Toggles whether or not the user will be prompted for a link's role when ;; interactively creating a link: (interactive) (setq *prompt-for-role* (not *prompt-for-role*)) (message (format "Prompt-for-Role is now %s." (if *prompt-for-role* "enabled" "disabled"))) ) (defun toggle-filename-compression () ;; Toggles whether or not filenames will be compressed back to their ;; original version using shell-vars when saving links.... (interactive) (setq *compress-save-filenames* (not *compress-save-filenames*)) (message (format "Filename Compression is now %s." (if *compress-save-filenames* "enabled" "disabled"))) ) (defun toggle-verbose-sections () ;; Toggles the state for printing out an audit trail of operations: (interactive) (setq *verbose-sections* (not *verbose-sections*)) (message (format "Verbose-Sections flag is now %s." (if *verbose-sections* "enabled" "disabled"))) ) ;(defun toggle-verbose-link-cues () ; ;; Toggles between the 'verbose' and 'terse' modes for showing link-cues ; ;; in buffers. This is only looked at when a file is read (or a buffer is ; ;; reverted). Existing buffers with link cues are not affected -- use the ; ;; functions show-{verbose/terse}-link-cues to change an existing buffer. ; (interactive) ; (setq *verbose-link-cues* (not *verbose-link-cues*)) ; (message (format "Verbose Link-Cues flag is now %s." ; (if *verbose-link-cues* "enabled" "disabled"))) ; ) ; ;(defun show-verbose-link-cues () ; ;; This forces all link-cues in the current-buffer to be in the verbose form: ; (interactive) ; (let ((*verbose-link-cues* t) ;; tmp rebinding of the global ; (mod-flag (buffer-modified-p)) ; (current-buffer (current-buffer)) ; ) ; (update-link-cues current-buffer) ; (unless mod-flag ; (set-buffer-modified-p mod-flag) ; ) ; )) ; ;(defun show-terse-link-cues () ; ;; This forces all link-cues in the current-buffer to be in the terse form: ; (interactive) ; (let ((*verbose-link-cues* nil) ;; tmp rebinding of the global ; (mod-flag (buffer-modified-p)) ; (current-buffer (current-buffer)) ; ) ; (update-link-cues current-buffer) ; (unless mod-flag ; (set-buffer-modified-p mod-flag) ; ) ; )) ; ;(defun update-link-cues (curr-buffer) ; ;; This function is called from the functions that toggle the verbosity ; ;; of embedded link cues. Basically, it modifies the link-cues in the given ; ;; buffer to be of the required verbosity. ; (interactive) ; (let* ((plist *zones-plist*) ; zone ; sec-buffer ; link-id ; ) ; (while plist ; (setq zone (nth 0 plist)) ; (setq object (nth 1 plist)) ; (setq sec-buffer (epoch::zone-buffer zone)) ; (cond ((and (eq sec-buffer curr-buffer) ; (equal (section-name zone) "*link*") ; ) ; (update-link-cue object zone) ; )) ; (setq plist (cdr (cdr plist))) ; ) ; )) ; ;(defun update-link-cue (link-id section) ; ;; For the given section, update the link-cue's text: ; (let* ((posn (epoch::zone-start section)) ; (old-text (zone-text section)) ; (link-attr (get-link-attributes link-id)) ; (link-type (get-type link-attr)) ; (new-text nil) ; ) ; (cond ((equal link-type 'Value_link) ; (setq new-text (return-value-link-cue-text link-id link-attr)) ; ) ; ((equal link-type 'Link) ; (setq new-text ; (return-link-cue-text ; link-id ; link-attr ; (if (eq section (get-src-section link-attr)) ; 'source ; 'dest ; ) ; )) ; )) ; (goto-char posn) ; (unless (equal old-text new-text) ; (unlock-zone (epoch::zone-at posn)) ; (insert new-text) ; (delete-char (length old-text)) ; (lock-zone (epoch::zone-at posn)) ; ) ; ))