X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/clg/blobdiff_plain/bdc0e300667f589ecdbee0ae6bf6dc51c06c73ae..06364bdbf32faa557bc9bc85049dcc15a42a9383:/gtk/gtk.lisp diff --git a/gtk/gtk.lisp b/gtk/gtk.lisp index cba75e9..5d1e3cb 100644 --- a/gtk/gtk.lisp +++ b/gtk/gtk.lisp @@ -1,5 +1,5 @@ ;; Common Lisp bindings for GTK+ v2.x -;; Copyright 1999-2005 Espen S. Johnsen +;; Copyright 1999-2006 Espen S. Johnsen ;; ;; Permission is hereby granted, free of charge, to any person obtaining ;; a copy of this software and associated documentation files (the @@ -20,7 +20,7 @@ ;; TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -;; $Id: gtk.lisp,v 1.45 2005-09-26 21:34:53 espen Exp $ +;; $Id: gtk.lisp,v 1.92 2008-04-10 20:38:49 espen Exp $ (in-package "GTK") @@ -45,28 +45,200 @@ (defun gtk-version () (format nil "Gtk+ v~A.~A.~A" major minor micro)))) (defun clg-version () - "clg CVS version") + "clg 0.94") -;;;; Initalization +;;;; Initalization and display handling + +(defparameter *event-polling-interval* 0.01) + +#?(or (featurep :clisp) (featurep :cmu) (and (sbcl>= 1 0 6) (sbcl< 1 0 15 6))) +(defun decompose-time (time) + (multiple-value-bind (sec subsec) (truncate *event-polling-interval*) + (values sec (truncate (* subsec 1e6))))) (defbinding (gtk-init "gtk_parse_args") () boolean "Initializes the library without opening the display." (nil null) (nil null)) -(defun clg-init (&optional display) - "Initializes the system and starts the event handling" +(defun clg-init (&optional display multi-threading-p) + "Initializes the system and starts event handling." (unless (gdk:display-get-default) - (gdk:gdk-init) - (unless (gtk-init) - (error "Initialization of GTK+ failed.")) - (prog1 - (gdk:display-open display) - (add-fd-handler (gdk:display-connection-number) :input #'main-iterate-all) - (setq *periodic-polling-function* #'main-iterate-all) - (setq *max-event-to-sec* 0) - (setq *max-event-to-usec* 1000)))) + #?(pkg-exists-p "gtk+-2.0" :atleast-version "2.8.0") + (progn + #+sbcl(sb-int:set-floating-point-modes :traps nil) + #+cmu(ext:set-floating-point-modes :traps nil)) + + (gdk:gdk-init) + (unless (gtk-init) + (error "Initialization of GTK+ failed.")) + + (if (not multi-threading-p) + (%init-async-event-handling display) + #+sb-thread(%init-multi-threaded-event-handling display) + #-sb-thread(error "Multi threading not supported on this platform"))) + (gdk:ensure-display display t)) + +(defun clg-init-with-threading (&optional display) + (clg-init display t)) + + +#?(and (sbcl>= 1 0 6) (sbcl< 1 0 15 6)) +;; A very minimal implementation of CLISP's socket-status +(defun socket-status (socket seconds microseconds) + (sb-alien:with-alien ((read-fds (sb-alien:struct sb-unix:fd-set))) + (let ((fd (sb-sys:fd-stream-fd (car socket)))) + (sb-unix:fd-zero read-fds) + (sb-unix:fd-set fd read-fds) + + (let ((num-fds-changed + (sb-unix:unix-fast-select + (1+ fd) (sb-alien:addr read-fds) nil nil + seconds microseconds))) + (unless (or (not num-fds-changed) (zerop num-fds-changed)) + (if (peek-char nil (car socket) nil) + :input + :eof)))))) + +(defun %init-async-event-handling (display) + (let ((style + #?(or (featurep :cmu) (sbcl< 1 0 6) (sbcl>= 1 0 15 6)) :fd-handler + #?-(or (featurep :cmu) (sbcl< 1 0 6) (sbcl>= 1 0 15 6)) nil)) + (when (and + (find-package "SWANK") + (not (eq (symbol-value (find-symbol "*COMMUNICATION-STYLE*" "SWANK")) style))) + (error "When running clg in Slime, the communication style ~S must be used in combination with asynchronous event handling on this platform. See the README file and for more information." style))) + + #?(or (featurep :cmu) (sbcl< 1 0 6) (sbcl>= 1 0 15 6)) + (progn + (signal-connect (gdk:display-manager) 'display-opened + #'(lambda (display) + (let ((fd (gdk:display-connection-number display))) + (unless (< fd 0) + (let ((handler (add-fd-handler + (gdk:display-connection-number display) + :input #'main-iterate-all))) + (signal-connect display 'closed + #'(lambda (is-error-p) + (declare (ignore is-error-p)) + (remove-fd-handler handler)))))))) + (setq *periodic-polling-function* #'main-iterate-all) + #?(or (featurep :cmu) (sbcl< 1 0 6)) + (multiple-value-setq (*max-event-to-sec* *max-event-to-usec*) + (decompose-time *event-polling-interval*)) + #?(sbcl>= 1 0 15 6) + (setq *periodic-polling-period* *event-polling-interval*)) + + #+(and clisp readline) + ;; Readline will call the event hook at most ten times per second + (setf readline:event-hook #'main-iterate-all) + + #?(or (featurep :clisp) (and (sbcl>= 1 0 6) (sbcl< 1 0 15 6))) + ;; When running in Slime we need to hook into the Swank server + ;; to handle events asynchronously. + (unless (and + (find-package "SWANK") + (let ((connection (symbol-value (find-symbol "*EMACS-CONNECTION*" "SWANK")))) + (when connection + (let ((read-from-emacs (symbol-function (find-symbol "READ-FROM-EMACS" "SWANK"))) + (stream (funcall (find-symbol "CONNECTION.SOCKET-IO" "SWANK") connection))) + (multiple-value-bind (sec usec) + (decompose-time *event-polling-interval*) + (setf + (symbol-function (find-symbol "READ-FROM-EMACS" "SWANK")) + #'(lambda () + (loop + (case (socket-status (cons stream :input) sec usec) + ((:input :eof) (return (funcall read-from-emacs))) + (otherwise (main-iterate-all))))))))))) + #-(and clisp readline) + (warn "Asynchronous event handling not supported on this platform. An explicit main loop has to be started.")) + + (gdk:display-open display)) + +#+sb-thread +(progn + (defvar *main-thread* nil) + + ;; Hopefully, when threading support is added to the Win32 port of + ;; SBCL in the future, this will work just out of the box. + #+win32 + (let ((done (sb-thread:make-waitqueue)) + (functions ()) + (results ())) + + ;; In Win32 all GDK calls have to be made from the main loop + ;; thread, so we add a timeout function which will poll for code and + ;; execute it. + + (defun funcall-in-main (function) + (if (or + (not *main-thread*) + (eq sb-thread:*current-thread* *main-thread*)) + (funcall function) + (gdk:with-global-lock + (push function functions) + (sb-thread:condition-wait done gdk:*global-lock*) + (pop results)))) + + ;; Will lock REPL on error, need to be fixed! + (defun %funcall-in-main-poll () + (when functions + (loop + for n from 0 + while functions + do (push (funcall (pop functions)) results) + finally (sb-thread:condition-notify done n))) + t)) + + (defmacro within-main-loop (&body body) + #-win32 `(gdk:with-global-lock ,@body) + #+win32 `(funcall-in-main #'(lambda () ,@body))) + + (defun %init-multi-threaded-event-handling (display) + (when (and + (find-package "SWANK") + (not (eq (symbol-value (find-symbol "*COMMUNICATION-STYLE*" "SWANK")) :spawn))) + (error "When running clg in Slime, the communication style :spawn must be used in combination with multi threaded event handling. See the README file and for more information.")) + (gdk:threads-init) + (let ((main-running (sb-thread:make-waitqueue))) + (gdk:with-global-lock + (setf *main-thread* + (sb-thread:make-thread + #'(lambda () + (gdk:with-global-lock + (gdk:display-open display) + #+win32(gdk:timeout-add-with-lock (/ *event-poll-interval* 1000) + #'%funcall-in-main-poll) + (sb-thread:condition-notify main-running) + (main))) + :name "gtk event loop")) + (sb-thread:condition-wait main-running gdk:*global-lock*))) + + ;; We need to hook into the Swank server to protect calls to GDK properly. + ;; This will *only* protect code entered directly in the REPL. + (when (find-package "SWANK") + (let ((repl-eval-hook (find-symbol "*SLIME-REPL-EVAL-HOOKS*" "SWANK"))) + (if repl-eval-hook + (push #'(lambda (form) + (within-main-loop (eval form))) + (symbol-value (find-symbol "*SLIME-REPL-EVAL-HOOKS*" "SWANK"))) + (warn "Your version of Slime does not have *SLIME-REPL-EVAL-HOOKS* so all calls to Gtk+ functions have to be explicit protected by wrapping them in a WITHIN-MAIN-LOOP form")))))) + +#-sb-thread +(defmacro within-main-loop (&body body) + `(progn ,@body)) + + + +;;; Generic functions + +(defgeneric add-to-radio-group (item1 item2)) +(defgeneric activate-radio-widget (item)) +(defgeneric (setf tool-item-tip-text) (tip-text tool-item)) +(defgeneric (setf tool-item-tip-private) (tip-private tool-item)) + ;;; Misc @@ -84,20 +256,20 @@ (defbinding get-default-language () (copy-of pango:language)) ;;; About dialog -#+gtk2.6 +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.6.0") (progn - (def-callback-marshal %about-dialog-activate-link-func - (nil (dialog about-dialog) (link (copy-of string)))) + (define-callback-marshal %about-dialog-activate-link-callback nil + (about-dialog (link string))) (defbinding about-dialog-set-email-hook (function) nil - ((callback %about-dialog-activate-link-func) pointer) + (%about-dialog-activate-link-callback callback) ((register-callback-function function) unsigned-int) - ((callback user-data-destroy-func) pointer)) + (user-data-destroy-callback callback)) (defbinding about-dialog-set-url-hook (function) nil - ((callback %about-dialog-activate-link-func) pointer) + (%about-dialog-activate-link-callback callback) ((register-callback-function function) unsigned-int) - ((callback user-data-destroy-func) pointer))) + (user-data-destroy-callback callback))) ;;; Acccel group @@ -118,7 +290,7 @@ (defun accel-group-connect (group accelerator function &optional flags) (defbinding accel-group-connect-by-path (group path function) nil (group accel-group) (path string) - ((make-callback-closure function) gclosure :return)) + ((make-callback-closure function) gclosure :in/return)) (defbinding %accel-group-disconnect (group gclosure) boolean (group accel-group) @@ -174,7 +346,7 @@ (defun accel-groups-activate (object accelerator) (multiple-value-bind (key modifiers) (parse-accelerator accelerator) (%accel-groups-activate object key modifiers))) -(defbinding accel-groups-from-object () (gslist accel-groups) +(defbinding accel-groups-from-object () (gslist accel-group) (object gobject)) (defbinding accelerator-valid-p (key &optional modifiers) boolean @@ -219,7 +391,7 @@ (defbinding accelerator-name () string (key unsigned-int) (modifiers gdk:modifier-type)) -#+gtk2.6 +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.6.0") (defbinding accelerator-get-label () string (key unsigned-int) (modifiers gdk:modifier-type)) @@ -245,8 +417,6 @@ (defbinding accel-label-refetch () boolean ;;; Accel map -(defbinding (accel-map-init "_gtk_accel_map_init") () nil) - (defbinding %accel-map-add-entry () nil (path string) (key unsigned-int) @@ -258,7 +428,7 @@ (defun accel-map-add-entry (path accelerator) (defbinding %accel-map-lookup-entry () boolean (path string) - ((make-instance 'accel-key) accel-key :return)) + ((make-instance 'accel-key) accel-key :in/return)) (defun accel-map-lookup-entry (path) (multiple-value-bind (found-p accel-key) (%accel-map-lookup-entry path) @@ -284,19 +454,17 @@ (defbinding accel-map-load () nil (defbinding accel-map-save () nil (filename pathname)) -(defcallback %accel-map-foreach-func - (nil - (callback-id unsigned-int) (accel-path (copy-of string)) - (key unsigned-int) (modifiers gdk:modifier-type) (changed boolean)) - (invoke-callback callback-id nil accel-path key modifiers changed)) +(define-callback-marshal %accel-map-foreach-callback nil + ((accel-path string) (key unsigned-int) + (modifiers gdk:modifier-type) (changed boolean)) :callback-id :first) (defbinding %accel-map-foreach (callback-id) nil (callback-id unsigned-int) - (%accel-map-foreach-func callback)) + (%accel-map-foreach-callback callback)) (defbinding %accel-map-foreach-unfiltered (callback-id) nil (callback-id unsigned-int) - (%accel-map-foreach-func callback)) + (%accel-map-foreach-callback callback)) (defun accel-map-foreach (function &optional (filter-p t)) (with-callback-function (id function) @@ -370,6 +538,56 @@ (defbinding alignment-set-padding () nil (right unsigned-int)) +;;; Assistant + +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.10.0") +(progn + (defbinding assistant-get-nth-page () widget + (assistant assistant) + (page-num int)) + + (defbinding %assistant-insert-page () int + (assistant assistant) + (page widget) + (pos int)) + + (defun assistant-insert-page (assistant page position &rest child-args) + (let ((pos (case position + (:first 0) + (:last -1) + (t position)))) + (prog1 + (%assistant-insert-page assistant page pos) + (init-child-slots assistant page child-args)))) + + (defun assistant-append-page (assistant page &rest child-args) + (apply #'assistant-insert-page assistant page :last child-args)) + + (defun assistant-prepend-page (assistant page &rest child-args) + (apply #'assistant-insert-page assistant page :first child-args)) + + (define-callback-marshal %assistant-page-func-callback int + ((current-page int))) + + (defbinding assistant-set-forward-page-func (assistant function) nil + (assistant assistant) + (%assistant-page-func-callback callback) + ((register-callback-function function) pointer-data) + (user-data-destroy-callback callback)) + + (defbinding assistant-add-action-widget () nil + (assistant assistant) + (child widget)) + + (defbinding assistant-remove-action-widget () nil + (assistant assistant) + (child widget)) + + (defbinding assistant-update-buttons-state () nil + (assistant assistant))) + + + ;;; Aspect frame @@ -381,11 +599,11 @@ (defun (setf bin-child) (child bin) (container-add bin child) child) -(defmethod compute-signal-function ((bin bin) signal function object) +(defmethod compute-signal-function ((bin bin) signal function object args) (declare (ignore signal)) (if (eq object :child) - #'(lambda (&rest args) - (apply function (bin-child bin) (rest args))) + #'(lambda (&rest emission-args) + (apply function (bin-child bin) (nconc (rest emission-args) args))) (call-next-method))) @@ -437,7 +655,8 @@ (defbinding box-set-child-packing () nil (defmethod initialize-instance ((button button) &rest initargs &key stock) (if stock - (apply #'call-next-method button :label stock :use-stock t initargs) + (apply #'call-next-method button + :label stock :use-stock t :use-underline t initargs) (call-next-method))) @@ -501,10 +720,13 @@ (defbinding check-menu-item-toggled () nil ;;; Color selection -(defbinding (color-selection-is-adjusting-p - "gtk_color_selection_is_adjusting") () boolean +(defbinding color-selection-is-adjusting-p () boolean (colorsel color-selection)) +(defbinding (color-selection-previous-color + "gtk_color_selection_get_previous_color") () nil + (colorsel color-selection) + ((make-instance 'gdk:color) gdk:color :in/return)) ;;; Color selection dialog -- no functions @@ -551,7 +773,7 @@ (defbinding combo-box-prepend-text () nil (combo-box combo-box) (text string)) -#+gtk2.6 +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.6.0") (defbinding combo-box-get-active-text () string (combo-box combo-box)) @@ -575,7 +797,7 @@ (defmethod initialize-instance ((combo-box-entry combo-box-entry) &key model) (defmethod shared-initialize ((dialog dialog) names &rest initargs &key button buttons) - (declare (ignore button buttons)) + (declare (ignore names button buttons)) (prog1 (call-next-method) (initial-apply-add dialog #'dialog-add-button initargs :button :buttons))) @@ -585,7 +807,7 @@ (defun dialog-response-id (dialog response &optional create-p error-p) "Returns a numeric response id" (if (typep response 'response-type) (response-type-to-int response) - (let ((responses (object-data dialog 'responses))) + (let ((responses (user-data dialog 'responses))) (cond ((and responses (position response responses :test #'equal))) (create-p @@ -595,7 +817,7 @@ (defun dialog-response-id (dialog response &optional create-p error-p) (1- (length responses))) (t (setf - (object-data dialog 'responses) + (user-data dialog 'responses) (make-array 1 :adjustable t :fill-pointer t :initial-element response)) 0))) @@ -604,9 +826,10 @@ (defun dialog-response-id (dialog response &optional create-p error-p) (defun dialog-find-response (dialog id) "Finds a symbolic response given a numeric id" - (if (< id 0) - (int-to-response-type id) - (aref (object-data dialog 'responses) id))) + (cond + ((not (numberp id)) id) + ((< id 0) (int-to-response-type id)) + ((aref (user-data dialog 'responses) id)))) (defmethod compute-signal-id ((dialog dialog) signal) @@ -614,15 +837,19 @@ (defmethod compute-signal-id ((dialog dialog) signal) (ensure-signal-id 'response dialog) (call-next-method))) -(defmethod compute-signal-function ((dialog dialog) signal function object) - (declare (ignore function object)) +(defmethod compute-signal-function ((dialog dialog) signal function object args) + (declare (ignore function object args)) (let ((callback (call-next-method)) (id (dialog-response-id dialog signal))) - (if id - #'(lambda (dialog response) - (when (= response id) - (funcall callback dialog))) - callback))) + (cond + (id + #'(lambda (dialog response) + (when (= response id) + (funcall callback dialog)))) + ((string-equal signal "response") + #'(lambda (dialog response) + (funcall callback dialog (dialog-find-response dialog response)))) + (callback)))) (defbinding dialog-run () nil (dialog dialog)) @@ -669,6 +896,7 @@ (defun dialog-add-action-widget (dialog widget &optional (response widget) (when (functionp response) (signal-connect dialog signal response :object object :after after)) (when default + (setf (widget-can-default-p widget) t) (%dialog-set-default-response dialog id)) widget)) @@ -686,14 +914,14 @@ (defbinding dialog-set-response-sensitive (dialog response sensitive) nil ((dialog-response-id dialog response nil t) int) (sensitive boolean)) -#+gtk2.6 +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.6.0") (defbinding alternative-dialog-button-order-p (&optional screen) boolean (screen (or null gdk:screen))) -#+gtk2.6 +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.6.0") (defbinding (dialog-set-alternative-button-order - "gtk_dialog_set_alternative_button_order_from_array") - (dialog new-order) + "gtk_dialog_set_alternative_button_order_from_array") + (dialog new-order) nil (dialog dialog) ((length new-order) int) ((map 'vector #'(lambda (response) @@ -701,7 +929,7 @@ (defbinding (dialog-set-alternative-button-order new-order) (vector int))) -#+gtk2.8 +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.8.0") (progn (defbinding %dialog-get-response-for-widget () int (dialog dialog) @@ -725,6 +953,12 @@ (defmethod (setf container-children) (children (dialog dialog)) (setf (container-children (dialog-vbox dialog)) children)) +;;; Drawing Area + +(defun drawing-area-scroll (drawing-area dx dy) + (gdk:window-scroll (widget-window drawing-area) dx dy)) + + ;;; Entry (defbinding entry-get-layout-offsets () nil @@ -743,19 +977,23 @@ (defbinding entry-text-index-to-layout-index () int ;;; Entry Completion -(def-callback-marshal %entry-completion-match-func - (boolean entry-completion string (copy-of tree-iter))) +(define-callback-marshal %entry-completion-match-callback boolean + (entry-completion string tree-iter)) (defbinding entry-completion-set-match-func (completion function) nil (completion entry-completion) - ((callback %entry-completion-match-func) pointer) + (%entry-completion-match-callback callback) ((register-callback-function function) unsigned-int) - ((callback user-data-destroy-func) pointer)) + (user-data-destroy-callback callback)) (defbinding entry-completion-complete () nil (completion entry-completion)) -#+gtk2.6 +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.12.0") +(defbinding entry-completion-get-completion-prefix () string + (completion entry-completion)) + +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.6.0") (defbinding entry-completion-insert-prefix () nil (completion entry-completion)) @@ -867,8 +1105,10 @@ (defmethod initialize-instance ((file-filter file-filter) &rest initargs (prog1 (call-next-method) (when pixbuf-formats - #-gtk2.6(warn "Initarg :PIXBUF-FORMATS not supportet in this version of Gtk") - #+gtk2.6(file-filter-add-pixbuf-formats file-filter)) + #?-(pkg-exists-p "gtk+-2.0" :atleast-version "2.6.0") + (warn "Initarg :PIXBUF-FORMATS not supportet in this version of Gtk") + #?(pkg-exists-p "gtk+-2.0" :atleast-version "2.6.0") + (file-filter-add-pixbuf-formats file-filter)) (initial-add file-filter #'file-filter-add-mime-type initargs :mime-type :mime-types) (initial-add file-filter #'file-filter-add-pattern @@ -883,18 +1123,18 @@ (defbinding file-filter-add-pattern () nil (filter file-filter) (pattern string)) -#+gtk2.6 +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.6.0") (defbinding file-filter-add-pixbuf-formats () nil (filter file-filter)) -(def-callback-marshal %file-filter-func (boolean file-filter-info)) +(define-callback-marshal %file-filter-callback boolean (file-filter-info)) (defbinding file-filter-add-custom (filter needed function) nil (filter file-filter) (needed file-filter-flags) - ((callback %file-filter-func) pointer) + (%file-filter-callback callback) ((register-callback-function function) unsigned-int) - ((callback user-data-destroy-func) pointer)) + (user-data-destroy-callback callback)) (defbinding file-filter-get-needed () file-filter-flags (filter file-filter)) @@ -935,7 +1175,7 @@ (defun create-image-widget (source &optional mask) ((or list vector) (make-instance 'image :pixmap source)) (gdk:pixmap (make-instance 'image :pixmap source :mask mask)))) -#+gtk2.8 +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.8.0") (defbinding image-clear () nil (image image)) @@ -1010,6 +1250,8 @@ (defun %add-activate-callback (widget signal function object after) (defmethod activate-radio-widget ((button radio-button)) (signal-emit button 'clicked)) +(defgeneric add-activate-callback (action function &key object after)) + (defmethod add-activate-callback ((button radio-button) function &key object after) (%add-activate-callback button 'clicked function object after)) @@ -1081,7 +1323,7 @@ (defbinding menu-item-toggle-size-allocate () nil ;;; Menu tool button -#+gtk2.6 +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.6.0") (defbinding menu-tool-button-set-arrow-tooltip () nil (menu-tool-button menu-tool-button) (tooltips tooltips) @@ -1091,19 +1333,39 @@ (defbinding menu-tool-button-set-arrow-tooltip () nil ;;; Message dialog -(defmethod initialize-instance ((dialog message-dialog) - &key (message-type :info) (buttons :close) - flags text #+gtk 2.6 secondary-text - transient-parent) - (setf - (slot-value dialog 'location) - (%message-dialog-new transient-parent flags message-type buttons)) +(defmethod allocate-foreign ((dialog message-dialog) &key (message-type :info) + button buttons flags transient-parent) + (let ((stock-buttons + (cond + ((and (not buttons) (not button)) + (case message-type + (:question :yes-no) + (t :ok))) + ((listp buttons) :none) + (t buttons)))) + (%message-dialog-new transient-parent flags message-type stock-buttons))) + + +(defmethod shared-initialize ((dialog message-dialog) names &rest initargs + &key message-type buttons button text + #?(pkg-exists-p "gtk+-2.0" :atleast-version "2.6.0") + secondary-text) + (declare (ignore names)) (when text (message-dialog-set-markup dialog text)) - #+gtk2.6 + #?(pkg-exists-p "gtk+-2.0" :atleast-version "2.6.0") (when secondary-text (message-dialog-format-secondary-markup dialog secondary-text)) - (call-next-method)) + (when (and (not buttons) (not button)) + (loop + for (key value) on initargs by #'cddr + when (and (eq key :signal) (eq (first value) :close)) + do (warn "Default button configuration changed from ~A to ~A" :close + (if (eq message-type :question) :yes-no :ok)) + (loop-finish))) + (if (typep buttons 'buttons-type) + (apply #'call-next-method dialog names (plist-remove :buttons initargs)) + (call-next-method))) (defbinding %message-dialog-new () pointer @@ -1117,12 +1379,12 @@ (defbinding message-dialog-set-markup () nil (message-dialog message-dialog) (markup string)) -#+gtk2.6 +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.6.0") (defbinding message-dialog-format-secondary-text () nil (message-dialog message-dialog) (text string)) -#+gtk2.6 +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.6.0") (defbinding message-dialog-format-secondary-markup () nil (message-dialog message-dialog) (markup string)) @@ -1191,13 +1453,26 @@ (defbinding toggle-button-toggled () nil ;;; Window (defmethod initialize-instance ((window window) &rest initargs - &key accel-group accel-groups) + &key display accel-group accel-groups) (declare (ignore accel-group accel-groups)) (prog1 - (call-next-method) + (if display + (apply #'call-next-method + window :screen (gdk:display-get-default-screen (gdk:ensure-display display)) initargs) + (call-next-method)) (initial-add window #'window-add-accel-group initargs :accel-group :accel-groups))) +#-debug-ref-counting +(defmethod print-object ((window window) stream) + (if (and + (proxy-valid-p window) + (slot-boundp window 'title) + (not (zerop (length (window-title window))))) + (print-unreadable-object (window stream :type t :identity nil) + (format stream "~S at 0x~X" + (window-title window) (pointer-address (foreign-location window)))) + (call-next-method))) (defbinding window-set-wmclass () nil (window window) @@ -1225,13 +1500,14 @@ (defbinding window-set-default-size (window width height) int (defbinding %window-set-geometry-hints () nil (window window) + (widget (or widget null)) (geometry gdk:geometry) (geometry-mask gdk:window-hints)) -(defun window-set-geometry-hints (window &key min-width min-height +(defun window-set-geometry-hints (window &key widget min-width min-height max-width max-height base-width base-height - width-inc height-inc min-aspect max-aspect - (gravity nil gravity-p) min-size max-size) + width-inc height-inc gravity + aspect (min-aspect aspect) (max-aspect aspect)) (let ((geometry (make-instance 'gdk:geometry :min-width (or min-width -1) :min-height (or min-height -1) @@ -1242,12 +1518,11 @@ (defun window-set-geometry-hints (window &key min-width min-height :width-inc (or width-inc 0) :height-inc (or height-inc 0) :min-aspect (or min-aspect 0) - :max-aspect (or max-aspect 0) - :gravity gravity)) + :max-aspect (or max-aspect 0))) (mask ())) - (when (or min-size min-width min-height) + (when (or min-width min-height) (push :min-size mask)) - (when (or max-size max-width max-height) + (when (or max-width max-height) (push :max-size mask)) (when (or base-width base-height) (push :base-size mask)) @@ -1255,9 +1530,10 @@ (defun window-set-geometry-hints (window &key min-width min-height (push :resize-inc mask)) (when (or min-aspect max-aspect) (push :aspect mask)) - (when gravity-p - (push :win-gravity mask)) - (%window-set-geometry-hints window geometry mask))) + (when gravity + (push :win-gravity mask) + (setf (gdk:geometry-gravity geometry) gravity)) + (%window-set-geometry-hints window widget geometry mask))) (defbinding window-list-toplevels () (glist (copy-of window)) "Returns a list of all existing toplevel windows.") @@ -1285,22 +1561,22 @@ (defbinding window-propagate-key-event () boolean (window window) (event gdk:key-event)) -#-gtk2.8 +#?-(pkg-exists-p "gtk+-2.0" :atleast-version "2.8.0") (defbinding window-present () nil (window window)) -#+gtk2.8 +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.8.0") (progn (defbinding %window-present () nil (window window)) - (defbinding %window-present-with-timestamp () nil + (defbinding %window-present-with-time () nil (window window) (timespamp unsigned-int)) (defun window-present (window &optional timestamp) (if timestamp - (%window-present-with-timestamp window timestamp) + (%window-present-with-time window timestamp) (%window-present window)))) (defbinding window-iconify () nil @@ -1366,7 +1642,7 @@ (defbinding window-get-frame-dimensions () nil (window window) (left int :out) (top int :out) (rigth int :out) (bottom int :out)) -(defbinding %window-get-icon-list () (glist gdk:pixbuf) +(defbinding %window-get-icon-list () (glist (copy-of gdk:pixbuf)) (window window)) (defbinding window-get-position () nil @@ -1412,10 +1688,14 @@ (defun (setf window-default-icon-list) (icons) (defbinding %window-set-default-icon () nil (icons (glist gdk:pixbuf))) +(defgeneric (setf window-default-icon) (icon)) + (defmethod (setf window-default-icon) ((icon gdk:pixbuf)) (%window-set-default-icon icon) icon) +(defgeneric (setf window-group) (group window)) + (defmethod (setf window-group) ((group window-group) (window window)) (window-group-add-window group window) group) @@ -1545,21 +1825,21 @@ (defun %ensure-notebook-child (notebook position) (t (notebook-get-nth-page notebook position)))) (defbinding (notebook-insert "gtk_notebook_insert_page_menu") - (notebook position child tab-label &optional menu-label) nil + (notebook position child &optional tab-label menu-label) nil (notebook notebook) (child widget) ((if (stringp tab-label) (make-instance 'label :label tab-label) - tab-label) widget) + tab-label) (or null widget)) ((if (stringp menu-label) (make-instance 'label :label menu-label) menu-label) (or null widget)) ((%ensure-notebook-position notebook position) position)) -(defun notebook-append (notebook child tab-label &optional menu-label) +(defun notebook-append (notebook child &optional tab-label menu-label) (notebook-insert notebook :last child tab-label menu-label)) -(defun notebook-prepend (notebook child tab-label &optional menu-label) +(defun notebook-prepend (notebook child &optional tab-label menu-label) (notebook-insert notebook :first child tab-label menu-label)) (defbinding notebook-remove-page (notebook page) nil @@ -1603,7 +1883,7 @@ (defun %notebook-current-page (notebook) (notebook-get-nth-page notebook (notebook-current-page-num notebook)))) (defun (setf notebook-current-page) (page notebook) - (setf (notebook-current-page notebook) (notebook-page-num notebook page))) + (setf (notebook-current-page-num notebook) (notebook-page-num notebook page))) (defbinding (notebook-tab-label "gtk_notebook_get_tab_label") (notebook page) widget @@ -1772,13 +2052,14 @@ (defbinding menu-attach () nil (top-attach unsigned-int) (bottom-attach unsigned-int)) -(def-callback-marshal %menu-position-func (nil (menu menu) (x int) (y int) (push-in boolean))) +(define-callback-marshal %menu-position-callback nil + (menu (x int) (y int) (push-in boolean))) (defbinding %menu-popup () nil (menu menu) (parent-menu-shell (or null menu-shell)) (parent-menu-item (or null menu-item)) - (callback-func (or null pointer)) + (callback (or null callback)) (callback-id unsigned-int) (button unsigned-int) (activate-time (unsigned 32))) @@ -1789,7 +2070,7 @@ (defun menu-popup (menu button activate-time &key callback parent-menu-shell (with-callback-function (id callback) (%menu-popup menu parent-menu-shell parent-menu-item - (callback %menu-position-func) id button activate-time)) + %menu-position-callback id button activate-time)) (%menu-popup menu parent-menu-shell parent-menu-item nil 0 button activate-time))) @@ -1817,22 +2098,22 @@ (defun (setf menu-active) (menu child) (%menu-set-active menu (%menu-position menu child)) child) -(defcallback %menu-detach-func (nil (widget widget) (menu menu)) - (funcall (object-data menu 'detach-func) widget menu)) +(define-callback %menu-detach-callback nil ((widget widget) (menu menu)) + (funcall (user-data menu 'detach-func) widget menu)) -(defbinding %menu-attach-to-widget () nil +(defbinding %menu-attach-to-widget (menu widget) nil (menu menu) (widget widget) - ((callback %menu-detach-func) pointer)) + (%menu-detach-callback callback)) (defun menu-attach-to-widget (menu widget function) - (setf (object-data menu 'detach-func) function) + (setf (user-data menu 'detach-func) function) (%menu-attach-to-widget menu widget)) (defbinding menu-detach () nil (menu menu)) -#+gtk2.6 +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.6.0") (defbinding menu-get-for-attach-widget () (copy-of (glist widget)) (widget widget)) @@ -1900,12 +2181,16 @@ (defbinding %table-set-col-spacings () nil (table table) (spacing unsigned-int)) -(defun (setf table-col-spacing) (spacing table &optional col) - (if col - (%table-set-col-spacing table col spacing) +(defun (setf table-column-spacing) (spacing table &optional column) + (if column + (%table-set-col-spacing table column spacing) (%table-set-col-spacings table spacing)) spacing) +(defun (setf table-col-spacing) (spacing table &optional col) + (warn "TABLE-COL-SPACING is deprecatet, use TABLE-COLUMN-SPACING instead") + (setf (table-column-spacing table col) spacing)) + (defbinding %table-get-col-spacing () unsigned-int (table table) (col unsigned-int)) @@ -1913,11 +2198,15 @@ (defbinding %table-get-col-spacing () unsigned-int (defbinding %table-get-default-col-spacing () unsigned-int (table table)) -(defun table-col-spacing (table &optional col) - (if col - (%table-get-col-spacing table col) +(defun table-column-spacing (table &optional column) + (if column + (%table-get-col-spacing table column) (%table-get-default-col-spacing table))) +(defun table-col-spacing (table &optional col) + (warn "TABLE-COL-SPACING is deprecatet, use TABLE-COLUMN-SPACING instead") + (table-column-spacing table col)) + ;;; Toolbar @@ -2017,7 +2306,7 @@ (defun (setf tool-item-proxy-menu-item) (menu-item menu-item-id tool-item) (%tool-item-set-proxy-menu-item menu-item-id tool-item menu-item) menu-item) -#+gtk2.6 +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.6.0") (defbinding tool-item-rebuild-menu () nil (tool-item tool-item)) @@ -2038,7 +2327,7 @@ (defbinding editable-insert-text (editable text &optional (position 0)) nil (editable editable) (text string) ((length text) int) - (position position :in-out)) + (position position :in/out)) (defun editable-append-text (editable text) (editable-insert-text editable text nil)) @@ -2107,7 +2396,7 @@ (defbinding %spin-button-spin () nil (defun spin-button-spin (spin-button value) (etypecase value - (real (%spin-button-spin spin-button :spin-user-defined value)) + (real (%spin-button-spin spin-button :user-defined value)) (spin-type (%spin-button-spin spin-button value 0)))) @@ -2213,12 +2502,6 @@ (defbinding %stock-item-copy () pointer (defbinding %stock-item-free () nil (location pointer)) -(defmethod reference-foreign ((class (eql (find-class 'stock-item))) location) - (%stock-item-copy location)) - -(defmethod unreference-foreign ((class (eql (find-class 'stock-item))) location) - (%stock-item-free location)) - (defbinding stock-add (stock-item) nil (stock-item stock-item) (1 unsigned-int)) @@ -2230,15 +2513,61 @@ (defbinding %stock-lookup () boolean (location pointer)) (defun stock-lookup (stock-id) - (let ((location - (allocate-memory (proxy-instance-size (find-class 'stock-item))))) - (unwind-protect - (when (%stock-lookup stock-id location) - (ensure-proxy-instance 'stock-item (%stock-item-copy location))) - (deallocate-memory location)))) + (with-memory (stock-item (foreign-size (find-class 'stock-item))) + (when (%stock-lookup stock-id stock-item) + (ensure-proxy-instance 'stock-item (%stock-item-copy stock-item))))) +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.8.0") +(progn + (define-callback-marshal %stock-translate-callback string ((path string))) -;;; Tooltips + (defbinding (stock-set-translate-function "gtk_stock_set_translate_func") + (domain function) nil + (domain string) + (%stock-translate-callback callback) + ((register-callback-function function) unsigned-int) + (user-data-destroy-callback callback))) + + +;;; Tooltip + +;; #?-(pkg-exists-p "gtk+-2.0" :atleast-version "2.12.0") +;; (progn +;; (defbinding %tooltip-set-markup () nil +;; tooltip +;; (markup string)) + +;; (defbinding %tooltip-set-text () nil +;; tooltip +;; (text string)) + +;; (defbinding %tooltip-set-icon () nil +;; tooltip +;; (icon gdk:pixbuf)) + +;; (defbinding %tooltip-set-from-stock-icon () nil +;; tooltip +;; (stock-id string) +;; icon-size) + +;; (defbinding %tooltip-set-custom () nil +;; tooltip +;; widget) + +;; (defun tooltip-set (tooltip value &key (markup t) (icon-size :button)) +;; (etypecase value +;; (string (if markup +;; (tooltip-set-markup tooltip value) +;; (tooltip-set-text tooltip value))) +;; (pixbuf (tooltip-set-icon tooltip value)) +;; (keyword (tooltip-set-icon-from-stock tooltip value icon-size)) + + + +;;; Tooltips + +;; GtkTooltips has been deprecated in favor of the new tooltip API +;; introduced in in GTK+ 2.12 (defbinding tooltips-enable () nil (tooltips tooltips)) @@ -2271,16 +2600,137 @@ (defbinding tooltips-get-info-from-tip-window () boolean ;;; Resource Files -(defbinding rc-add-default-file (filename) nil - ((namestring (truename filename)) string)) +(defbinding rc-get-style () style + (widget widget)) + +(defbinding rc-get-style-by-paths (&key path class-path class) style + (path (or null string)) + (class-path (or null string)) + (class gtype)) -(defbinding rc-parse (filename) nil - ((namestring (truename filename)) string)) +(defbinding rc-parse () nil + (filename pathname)) (defbinding rc-parse-string () nil (rc-string string)) -(defbinding rc-reparse-all () nil) +(defbinding %rc-reparse-all () boolean) + +(defbinding %rc-reparse-all-for-settings () boolean + (settings settings) + (force-load-p boolean)) + +(defun rc-reparse-all (&optional setting force-load-p) + (if setting + (%rc-reparse-all-for-settings setting force-load-p) + (%rc-reparse-all))) + +(defbinding rc-reset-styles () nil + (settings settings)) + +(defbinding rc-add-default-file () nil + (filename pathname)) + +(defbinding rc-get-default-files () + (copy-of (null-terminated-vector (copy-of string)))) + +(defbinding rc-get-module-dir () string) + +(defbinding rc-get-im-module-path () string) + +(defbinding rc-get-im-module-file () string) + +(defbinding rc-get-theme-dir () string) + + +;;; Settings + +(defbinding (settings-get "gtk_settings_get_for_screen") + (&optional (screen (gdk:display-get-default-screen))) settings + (screen gdk:screen)) + + +;;; Plug and Socket + +(defbinding socket-add-id () nil + (socket socket) + (id gdk:native-window)) + +(defbinding %plug-new () pointer + (id gdk:native-window)) + +(defmethod allocate-foreign ((plug plug) &key id) + (%plug-new (or id 0))) + + +;;; Link button + +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.10.0") +(progn + (define-callback-marshal %link-button-uri-callback nil (link-button (link string))) + + (defbinding link-button-set-uri-hook (function) pointer + (%link-button-uri-callback callback) + ((register-callback-function function) unsigned-int) + (user-data-destroy-callback callback))) + + +;;; Builder + +#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.12.0") +(progn + (defmethod initialize-instance ((builder builder) &key interface + (connect-signals t) (package *package*)) + (call-next-method) + (etypecase interface + (null) + (string (builder-add-from-string builder interface)) + (pathname (builder-add-from-file builder interface))) + (when connect-signals + (builder-connect-signals builder package))) + + + (defbinding builder-add-from-file () boolean + builder + pathname + (nil gerror-signal :out)) + + (defbinding builder-add-from-string () boolean + builder + (buffer string) + (-1 int) ; TODO: add gsize type + (nil gerror-signal :out)) + + (defbinding builder-get-object () gobject + builder + (name string)) + + (defbinding builder-get-objects () (gslist gobject) + builder) + + (defun intern-with-package-prefix (name default-package) + (let ((pos (position #\: name))) + (if pos + (intern + (string-upcase (subseq name (1+ pos))) + (string-upcase (subseq name 0 pos))) + (intern (string-upcase name) default-package)))) + + (define-callback %builder-connect-function nil + (builder (object gobject) (signal-name string) (handler-name string) + (connect-object gobject) connect-flags (package user-data-id)) + (format t "Connect signal ~A for ~A to ~A in default package ~A with flags ~A~%" signal-name object handler-name (find-user-data package) connect-flags) + (signal-connect + object signal-name + (intern-with-package-prefix handler-name (find-user-data package)) + :object (or connect-object object) :after (find :after connect-flags))) + + (defbinding %builder-connect-signals-full (builder package) nil + builder + (%builder-connect-function callback) + (package user-data-id)) + + (defun builder-connect-signals (builder &optional (package *package*)) + (with-user-data (id package) + (%builder-connect-signals-full builder id)))) -(defbinding rc-get-style () style - (widget widget))