;;; uko-search.el -- search modes

;; Copyright (C) 2003 Michael Kuehn, Universität Koblenz-Landau

;; Author: Michael Kuehn <kuehn@uni-koblenz.de>
;; Maintainer: Michael Kuehn <kuehn@uni-koblenz.de>
;; Created: March 2003
;; 
;; $Id: uko-search.el,v 1.2 2003/08/26 10:37:35 deee Exp $



(defvar uko-incremental-search-string ""
  "Temporary incremental search string.")

(defvar uko-incremental-search-extent nil
  "Extent used for matches in incremental search.")

(defvar uko-incremental-search-start nil
  "Temporary buffer for original incremental search start point.")

(defvar uko-incremental-search-stack nil
  "Stack for back jumps in incremental search.")

(defvar uko-incremental-search-begin-of-word nil
  "Should incremental search only look for matches at the begin of a line?")



(defun uko-char-ambiguous-key (char)
  "Return the ambiguous key mapped to CHAR."
  (let ((downchar (downcase char)))
    (cond
     ((member downchar (car uko-keyboard-layout)) 1)
     ((member downchar (cadr uko-keyboard-layout)) 2)
     ((member downchar (caddr uko-keyboard-layout)) 3)
     (t nil)
     ) ) )

(defun uko-code-to-regex (code &optional eow)
  (if code
      (append "[" (concat (nth (- (char-to-int (car code)) 49) uko-keyboard-layout)) "]"
	      (uko-code-to-regex (cdr code) eow) )
    (if eow '(?$) '())
    ) )
      
(defun uko-search-forward (code &optional bow eow)
  (search-forward-regexp 
   (concat 
    (if bow '(?^) '())
    (uko-code-to-regex (string-to-list code) eow)
    ) 
   nil t)
  )

(defun uko-search-backward (code &optional bow eow)
  (search-backward-regexp 
   (concat 
    (if bow "\<" "")
    (uko-code-to-regex (string-to-list code) eow)
    ) 
   nil t)
  )

(defun uko-search-last-direction nil)

(defun uko-search-forward-buffer (code buffer &optional bol eol)
  (save-current-buffer
    (set-buffer buffer)
    (when (eq uko-search-last-direction 'backward)
      (forward-word)
      )
    (setq uko-search-last-direction 'forward)
    (uko-search-forward code bol eol)
    ) )

(defun uko-search-backward-buffer (code buffer &optional bol eol)
  (save-current-buffer
    (set-buffer buffer)
    (when (eq uko-search-last-direction 'forward)
      (backward-word)
      )
    (setq uko-search-last-direction 'backward)
    (uko-search-backward code bol eol)
    ) )

;; Incremental search routines

(defun uko-incremental-search (&optional bow)
  "Enter incremental search mode in UKO-II."
  (interactive)
  (setq uko-incremental-search-begin-of-word bow)
  (setq uko-incremental-search-stack nil)
  (uko-set-incremental-search-state)
  )

(defun uko-set-incremental-search-state ()
  "Set keyboard for incremental search."
  (setq uko-incremental-search-string "")
  (setq uko-incremental-search-extent (make-extent (point) (point)))
  (setq uko-incremental-search-start (point))
  (set-extent-property uko-incremental-search-extent 'face 'isearch)
  (uko-display-keyboard-buttons
    (append (mapcar 'uko-keyboard-layout-button uko-keyboard-layout)
	    (list "     Isearch") ) )
  (set-extent-property uko-expand-extent 'keymap uko-incremental-search-keymap)
  )

(defun uko-incremental-search-command ()
  "Enter incremental search mode in UKO-II."
  (interactive)
  (uko-set-incremental-search-command-state)
  )

(defun uko-set-incremental-search-command-state ()
  "Set keyboard for incremental search."
  (uko-display-keyboard-buttons (list "Back" "Done" "Next" (list "Isearch" "Cancel")))
  (set-extent-property uko-expand-extent 'keymap uko-incremental-search-command-keymap)
  )

(defvar uko-incremental-search-keymap (make-sparse-keymap)
  "The keymap for the UKO-II incremental search letter keymap.")
(define-key uko-incremental-search-keymap uko-physical-switch-0 'uko-incremental-search-command)
(define-key uko-incremental-search-keymap uko-physical-switch-1 'uko-incremental-search-letter-1)
(define-key uko-incremental-search-keymap uko-physical-switch-2 'uko-incremental-search-letter-2)
(define-key uko-incremental-search-keymap uko-physical-switch-3 'uko-incremental-search-letter-3)

(defun uko-incremental-search-letter (key)
  "Continue incremental search with KEY."
  (setq uko-incremental-search-string 
	(concat uko-incremental-search-string (int-to-string key)) )
  (let ((akey (uko-char-ambiguous-key (char-after (point)))))

    (if 
      ; Is KEY a continuation of a previous match at point?
      (and akey (= key akey))
      ; Yes - extend the search string and match with KEY.
      (progn
	(forward-char)
	(set-extent-endpoints uko-incremental-search-extent
	  (extent-start-position uko-incremental-search-extent)
	  (1+ (extent-end-position uko-incremental-search-extent)) )
	(setq uko-incremental-search-stack (cons (cons (point) uko-incremental-search-string) uko-incremental-search-stack))
	)

    ; No - search for the search string extended by key.
    ; If this is unsuccessful, shorten the search string by KEY again and beep.
    (let ((find (uko-search-forward uko-incremental-search-string uko-incremental-search-begin-of-word)))
      (if find
	  (progn
	    (set-extent-endpoints uko-incremental-search-extent 
		(- find (length uko-incremental-search-string)) find)
	    (setq uko-incremental-search-stack (cons (cons (point) uko-incremental-search-string) uko-incremental-search-stack)) )
	(progn
	  (setq uko-incremental-search-string (substring uko-incremental-search-string 0 -1) )
	  (ding)
	) ) )
    ) ) )

(defun uko-incremental-search-letter-1 ()
  (interactive)
  (uko-incremental-search-letter 1)
  )

(defun uko-incremental-search-letter-2 ()
  (interactive)
  (uko-incremental-search-letter 2)
  )

(defun uko-incremental-search-letter-3 ()
  (interactive)
  (uko-incremental-search-letter 3)
  )

(defvar uko-incremental-search-command-keymap (make-sparse-keymap)
  "The keymap for the UKO-II incremental search command keymap.")
(define-key uko-incremental-search-command-keymap uko-physical-switch-0 'uko-incremental-search-cancel)
(define-key uko-incremental-search-command-keymap uko-physical-switch-1 'uko-incremental-search-back)
(define-key uko-incremental-search-command-keymap uko-physical-switch-2 'uko-incremental-search-done)
(define-key uko-incremental-search-command-keymap uko-physical-switch-3 'uko-incremental-search-next)

(defun uko-incremental-search-next ()
  "Find next match with current search string."
  (interactive)
  (let ((here (point)))
    (setq uko-incremental-search-stack (cons (cons (point) uko-incremental-search-string) uko-incremental-search-stack))
    (goto-char (extent-start-position uko-incremental-search-extent))
    (forward-char)
    (let ((find (uko-search-forward uko-incremental-search-string uko-incremental-search-begin-of-word)))
      (if find
	  (set-extent-endpoints 
	   uko-incremental-search-extent 
	   (- find (length uko-incremental-search-string))
	   find)
	(progn
	  (setq uko-incremental-search-stack (cdr uko-incremental-search-stack))
	  (goto-char here) )
	) ) ) )

(defun uko-incremental-search-done ()
  "Return from incremental search."
  (interactive)
  (delete-extent uko-incremental-search-extent)
  (setq uko-incremental-search-extent nil)
  (uko-set-default-state)
  )
  
(defun uko-incremental-search-back ()
  "Undo last incremental search step."
  (interactive)
  (if (null uko-incremental-search-stack)
      (ding)
    (setq uko-incremental-search-stack (cdr uko-incremental-search-stack))
    (if uko-incremental-search-stack
	(let ((last (car uko-incremental-search-stack)))
	  (setq uko-incremental-search-string (cdr last))
	  (goto-char (car last))
	  (beginning-of-line)
	  (set-extent-endpoints uko-incremental-search-extent (point) (car last))
	  (goto-char (car last))
	  )
      (goto-char 1)
      (setq uko-incremental-search-string "")
      ) ) )

(defun uko-incremental-search-cancel ()
  "Cancel incremental search."
  (interactive)
  (delete-extent uko-incremental-search-extent)
  (setq uko-incremental-search-extent nil)
  (goto-char uko-incremental-search-start)
  (uko-set-default-state)
  )
  
