;;; dialog-gtk.el --- Dialog-box support for XEmacs w/GTK primitives

;; Copyright (C) 2000 Free Software Foundation, Inc.

;; Maintainer: William M. Perry <wmperry@gnu.org>
;; Keywords: extensions, internal, dumped

;; This file is part of XEmacs.

;; XEmacs 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.

;; XEmacs 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 XEmacs; see the file COPYING.  If not, write to the 
;; Free Software Foundation, 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;;; Synched up with: Not in FSF.

;;; Commentary:

;; This file is dumped with XEmacs (when dialog boxes are compiled in).

(require 'cl)
(require 'gtk-password-dialog)
(require 'gtk-file-dialog)

(defun popup-builtin-open-dialog (keys)
  ;; Allowed keywords are:
  ;;
  ;;  :initial-filename fname
  ;;  :initial-directory dir
  ;;  :filter-list (filter-desc filter ...)
  ;;  :directory t/nil
  ;;  :title string
  ;;  :allow-multi-select t/nil
  ;;  :create-prompt-on-nonexistent t/nil
  ;;  :overwrite-prompt t/nil
  ;;  :file-must-exist t/nil
  ;;  :no-network-button t/nil
  ;;  :no-read-only-return t/nil
  (let ((initial-filename (plist-get keys :initial-filename))
	(clicked-ok nil)
	(filename nil)
	(widget nil))
    (setq widget (gtk-file-dialog-new
 		  :directory (plist-get keys :directory)
		  :callback `(lambda (f)
			       (setq clicked-ok t
				     filename f))
		  :initial-directory (or (plist-get keys :initial-directory nil)
					 (if initial-filename
					     (file-name-directory initial-filename)
					   default-directory))
		  :filter-list (plist-to-alist
				(plist-get keys :filter-list nil))
		  :file-must-exist (plist-get keys :file-must-exist nil)))

    (gtk-signal-connect widget 'destroy (lambda (obj data) (gtk-main-quit)))

    (gtk-window-set-transient-for widget (frame-property nil 'shell-widget))
    (gtk-widget-show-all widget)
    (gtk-main)
    (if (not clicked-ok)
	(signal 'quit nil)
      filename)))

(defalias 'popup-builtin-save-as-dialog 'popup-builtin-open-dialog)

(defun popup-builtin-color-dialog (keys)
  ;; Allowed keys:
  ;;   :initial-color COLOR
  (let ((initial-color (or (plist-get keys :initial-color) "white"))
	(title (or (plist-get keys :title "Select color...")))
	(dialog nil)
	(clicked-ok nil)
	(color nil))
    (setq dialog (gtk-color-selection-dialog-new title))
    (gtk-signal-connect
     (gtk-color-selection-dialog-ok-button dialog) 'clicked
     (lambda (button colorsel)
       (gtk-widget-hide-all dialog)
       (setq color (gtk-color-selection-get-color colorsel)
	     clicked-ok t)
       (gtk-main-quit))
     (gtk-color-selection-dialog-colorsel dialog))

    (gtk-signal-connect
     (gtk-color-selection-dialog-cancel-button dialog) 'clicked
     (lambda (&rest ignored)
       (gtk-main-quit)))

    (put dialog 'modal t)
    (put dialog 'type 'dialog)
    (gtk-window-set-transient-for dialog (frame-property nil 'shell-widget))

    (unwind-protect
	(progn
	  (gtk-widget-show-now dialog)
	  (gtk-main))
      '(gtk-widget-destroy dialog))
    (if (not clicked-ok)
	(signal 'quit nil))
    ;; Need to convert from (R G B A) to #rrggbb
    (format "#%02x%02x%02x"
	    (* 256 (nth 0 color))
	    (* 256 (nth 1 color))
	    (* 256 (nth 2 color)))))

(defun popup-builtin-password-dialog (keys)
  ;; Format is (default callback :keyword value)
  ;; Allowed keywords are:
  ;;
  ;;  :title string
  :;  :prompt string
  ;;  :default string
  ;;  :verify boolean
  ;;  :verify-prompt string
  (let* ((default (plist-get keys :default))
	 (dialog nil)
	 (clicked-ok nil)
	 (passwd nil)
	 (info nil)
	 (generic-cb (lambda (x)
		       (setq clicked-ok t
			     passwd x))))

    ;; Convert the descriptor to keywords and create the dialog
    (setq info (copy-list keys)
	  info (plist-put info :callback generic-cb)
	  info (plist-put info :default default)
	  dialog (apply 'gtk-password-dialog-new info))

    ;; Clicking any button or closing the box exits the main loop.
    (gtk-signal-connect (gtk-password-dialog-ok-button dialog)
			'clicked
			(lambda (&rest ignored)
			  (gtk-main-quit)))

    (gtk-signal-connect (gtk-password-dialog-cancel-button dialog)
			'clicked
			(lambda (&rest ignored)
			  (gtk-main-quit)))

    (gtk-signal-connect dialog
			'delete-event
			(lambda (&rest ignored)
			  (gtk-main-quit)))

    (gtk-widget-grab-focus (gtk-password-dialog-entry-widget dialog))

    ;; Make us modal...
    (put dialog 'modal t)
    (gtk-window-set-transient-for dialog (frame-property nil 'shell-widget))

    ;; Realize the damn thing & wait for some action...
    (gtk-widget-show-all dialog)
    (gtk-main)

    (if (not clicked-ok)
	(signal 'quit nil))

    (gtk-widget-destroy dialog)
    passwd))

(defun popup-builtin-question-dialog (keys)
  ;; Allowed keywords:
  ;;   :question STRING
  ;;   :buttons  BUTTONDESC
  (let ((title (or (plist-get keys :title) "Question"))
	(buttons-descr (plist-get keys :buttons))
	(question (or (plist-get keys :question) "Question goes here..."))
	(dialog nil)			; GtkDialog
	(buttons nil)			; List of GtkButton objects
	(activep t)
	(flushrightp nil)
	(errp t))
    (if (not buttons-descr)
	(error 'syntax-error
	       "Dialog descriptor must supply at least one button"))

    ;; Do the basics - create the dialog, set the window title, and
    ;; add the label asking the question.
    (unwind-protect
	(progn
	  (setq dialog (gtk-dialog-new))
	  (gtk-window-set-title dialog title)
	  (gtk-container-set-border-width dialog 3)
	  (gtk-box-set-spacing (gtk-dialog-vbox dialog) 5)
	  (gtk-container-add (gtk-dialog-vbox dialog) (gtk-label-new question))

	  ;; Create the buttons.
	  (mapc (lambda (button)
		  ;; Handle flushright buttons
		  (if (null button)
		      (setq flushrightp t)

		    ;; More sanity checking first of all.
		    (if (not (vectorp button))
			(error "Button descriptor is not a vector: %S" button))

		    (if (< (length button) 3)
			(error "Button descriptor is too small: %S" button))

		    (push (gtk-button-new-with-label (aref button 0)) buttons)

		    ;; Need to detect what flavor of descriptor it is.
		    (if (not (keywordp (aref button 2)))
			;; Simple style... just [ name callback activep ]
			;; We ignore the 'suffix' entry, because that is what
			;; the X code does.
			(setq activep (aref button 2))
		      (let ((ctr 2)
			    (len (length button)))
			(if (logand len 1)
			    (error
			     "Button descriptor has an odd number of keywords and values: %S"
			     button))
			(while (< ctr len)
			  (if (eq (aref button ctr) :active)
			      (setq activep (aref button (1+ ctr))
				    ctr len))
			  (setq ctr (+ ctr 2)))))
		    (gtk-widget-set-sensitive (car buttons) (eval activep))
		    
		    ;; Apply the callback
		    (gtk-signal-connect
		     (car buttons) 'clicked
		     (lambda (button data)
		       (push (make-event 'misc-user
					 (list 'object (car data)
					       'function
					       (if (symbolp (car data))
						   'call-interactively
						 'eval)))
			     unread-command-events)
		       (gtk-main-quit)
		       t)
		     (cons (aref button 1) dialog))

		    (gtk-widget-show (car buttons))
		    (funcall (if flushrightp 'gtk-box-pack-end 'gtk-box-pack-start)
			     (gtk-dialog-action-area dialog) (car buttons)
			     nil t 2)))
		buttons-descr)

	  ;; Make sure they can't close it with the window manager
	  (gtk-signal-connect dialog 'delete-event (lambda (&rest ignored) t))
	  (gtk-window-set-transient-for dialog (frame-property nil 'shell-widget))
	  (put dialog 'type 'dialog)
	  (put dialog 'modal t)
	  (gtk-widget-show-all dialog)
	  (gtk-main)
	  (gtk-widget-destroy dialog)
	  (setq errp nil))
      (if (not errp)
	  ;; Nothing, we successfully showed the dialog
	  nil
	;; We need to destroy all the widgets, just in case.
	(mapc 'gtk-widget-destroy buttons)
	(gtk-widget-destroy dialog)))))

(defun gtk-make-dialog-box-internal (type keys)
  (case type
    (file
     (popup-builtin-open-dialog keys))
    (password
     (popup-builtin-password-dialog keys))
    (question
     (popup-builtin-question-dialog keys))
    (color
     (popup-builtin-color-dialog keys))
    (find
     )
    (font
     )
    (replace
     )
    (mswindows-message
     ;; This should really be renamed!
     )
    (print
     )
    (page-setup
     )
    (print-setup
     )
    (default
      (error "Unknown type of dialog: %S" type))))

(provide 'dialog-gtk)
