4d80b7919941afc99b424764bd828da544ea6eba
[cl-gtk2.git] / gtk / gtk.demo.lisp
1 (defpackage :gtk-demo
2   (:use :cl :gtk :gdk :gobject :iter)
3   (:export #:demo))
4
5 (in-package :gtk-demo)
6
7 (defparameter *src-location* (asdf:component-pathname (asdf:find-system :cl-gtk2-gtk)))
8
9 (defclass link-text-tag (text-tag)
10   ()
11   (:metaclass gobject-class))
12
13 (defun make-link-fn-tag (buffer fn)
14   (let ((tag (make-instance 'link-text-tag :foreground "blue" :underline :single)))
15     (text-tag-table-add (text-buffer-tag-table buffer) tag)
16     (connect-signal tag "event"
17                     (lambda (tag object event it)
18                       (declare (ignore tag object it))
19                       (when (and (eq (event-type event) :button-release)
20                                  (eq (event-button-button event) 1))
21                         (when fn
22                           (funcall fn)))))
23     tag))
24
25 (defun get-page (name)
26   (or (get name 'demo-page)
27       (get 'page-404 'demo-page)))
28
29 (defun (setf get-page) (page name)
30   (setf (get name 'demo-page) page))
31
32 (defmacro def-demo-page ((name &key (index 'index)) &body body)
33   `(setf (get-page ',name)
34          '(,@(when index (list `(:p (:link "To main" ,index))))
35            ,@body)))
36
37 (def-demo-page (page-404)
38   (:p "Non-existent page"))
39
40 (def-demo-page (index :index nil)
41   (:p (:b "cl-gtk2 demonstration"))
42   (:p "")
43   (:p "This demo application is a demonstration of what cl-gtk2 can do. You can click on any of blue underlined links to invoke the demonstration.")
44   (:p "")
45   (:p "You may try these demos:")
46   (:ol (:fn "Demonstrates usage of tree store" test-tree-store)
47        (:fn "Simple test of packing widgets into GtkTable"
48             table-packing)
49        (:fn "Test of GtkStatusbar" test-statusbar)
50        (:fn "Not working example of GtkEntryCompletion"
51             test-entry-completion)
52        (:fn "Simple test of non-GObject subclass of GtkWindow"
53             test-custom-window)
54        (:fn "Testing progress-bar" test-progress-bar)
55        (:fn "Simple test of GtkAssistant wizard" test-assistant)
56        (:fn "Using GtkImage with stock icon" test-image)
57        (:fn "Test of GtkCalendar" test-calendar)
58        (:fn "Test of GtkBuilder" test-builder)
59        (:fn "Test of GtkColorButton" test-color-button)
60        (:fn "Test of UI Markup" test-ui-markup)
61        (:fn "Test of scale button with icons" test-scale-button)
62        (:fn "Testing GtkComboBox" test-combo-box)
63        (:fn "Advanced demo: show s-expression tree structure"
64             demo-treeview-tree)
65        (:fn "Test of child-property usage" test-box-child-property)
66        (:fn "Demonstrates usage of list store" test-list-store)
67        (:fn "Test various gdk primitives" test-gdk)
68        (:fn "Test GtkNotebook" test-notebook)
69        (:fn "More advanced example: text editor with ability to evaluate lisp expressions"
70             demo-text-editor)
71        (:fn "(not completed)" test-pixbuf)
72        (:fn "Testing GtkTextEntry" test-entry)
73        (:fn "Test of treeview with CL-GTK2-GTK:ARRAY-LIST-STORE"
74             test-treeview-list)
75        (:fn "Test of GtkFileChooser" test-file-chooser)
76        (:fn "Test of GtkColorSelection" test-color-selection)
77        (:fn "Test of GtkTextView" test-text-view)
78        (:fn "A simple test of 'on-expose' event" test)
79        (:fn "Show slots of a given class" demo-class-browser)
80        (:fn "Testing GtkUIManager" test-ui-manager)
81        (:fn "GtkFontChooser" test-font-chooser)))
82
83 (defun clear-text-tag-table (table)
84   (let (tags)
85     (text-tag-table-foreach table
86                             (lambda (tag)
87                               (push tag tags)))
88     (iter (for tag in tags)
89           (text-tag-table-remove table tag))))
90
91 (defun fill-demo-text-buffer (buffer text-view &optional (page 'index))
92   (declare (ignorable text-view))
93   (clear-text-tag-table (text-buffer-tag-table buffer))
94   (setf (text-buffer-text buffer) "")
95   (text-tag-table-add (text-buffer-tag-table buffer) (make-instance 'text-tag :name "bold" :weight 700))
96   (labels ((insert-text (text)
97              (text-buffer-insert buffer text))
98            (insert-link (text fn)
99              (let ((offset (text-iter-offset (text-buffer-get-end-iter buffer))))
100                (text-buffer-insert buffer text)
101                (text-buffer-apply-tag buffer (make-link-fn-tag buffer fn)
102                                       (text-buffer-get-iter-at-offset buffer offset)
103                                       (text-buffer-get-end-iter buffer))))
104            (insert-newline ()
105              (text-buffer-insert buffer (format nil "~%")))
106            (process-paragraph (node)
107              (map nil #'process (rest node))
108              (insert-newline))
109            (process-link (node)
110              (insert-link (second node) (lambda () (fill-demo-text-buffer buffer text-view (third node)))))
111            (process-fn (node)
112              (insert-link (second node) (third node)))
113            (process-ul (node)
114              (iter (for n in (rest node))
115                    (for i from 1)
116                    (insert-text "* ")
117                    (process n)
118                    (insert-newline)))
119            (process-ol (node)
120              (iter (for n in (rest node))
121                    (for i from 1)
122                    (insert-text (format nil "~A. " i))
123                    (process n)
124                    (insert-newline)))
125            (process-bold (node)
126              (let ((offset (text-iter-offset (text-buffer-get-end-iter buffer))))
127                (map nil #'process (rest node))
128                (text-buffer-apply-tag buffer "bold" (text-buffer-get-iter-at-offset buffer offset) (text-buffer-get-end-iter buffer))))
129            (process (node)
130              (cond
131                ((stringp node) (insert-text node))
132                ((and (listp node) (eq (car node) :p)) (process-paragraph node))
133                ((and (listp node) (eq (car node) :link)) (process-link node))
134                ((and (listp node) (eq (car node) :fn)) (process-fn node))
135                ((and (listp node) (eq (car node) :ul)) (process-ul node))
136                ((and (listp node) (eq (car node) :ol)) (process-ol node))
137                ((and (listp node) (eq (car node) :b)) (process-bold node))
138                ((listp node) (map nil #'process node))
139                (t (error "Do not know how to proceed")))))
140     (process (get-page page))))
141
142 (defun make-demo-text-buffer (text-view)
143   (let ((buffer (make-instance 'text-buffer)))
144     (fill-demo-text-buffer buffer text-view)
145     buffer))
146
147 (defvar *active-tag* nil)
148
149 (defun tv-motion-notify (tv event)
150   (multiple-value-bind (x y)
151       (text-view-window-to-buffer-coords tv :text
152                                          (round (event-motion-x event)) (round (event-motion-y event)))
153     (let ((it (text-view-get-iter-at-location tv x y)))
154       (if it
155           (let ((tags (text-iter-tags it)))
156             (if tags
157                 (loop
158                    for tag in tags
159                    when (typep tag 'link-text-tag)
160                    do (progn
161                         (when *active-tag*
162                           (setf (text-tag-foreground *active-tag*) "blue"
163                                 *active-tag* nil))
164                         (setf (gdk-window-cursor (text-view-get-window tv :text))
165                               (cursor-new-for-display (drawable-display (text-view-get-window tv :text))
166                                                       :hand2)
167                               *active-tag* tag
168                               (text-tag-foreground *active-tag*) "red")))
169                 (progn
170                   (setf (gdk-window-cursor (text-view-get-window tv :text)) nil)
171                   (when *active-tag*
172                     (setf (text-tag-foreground *active-tag*) "blue"
173                           *active-tag* nil)))))
174           (progn
175             (setf (gdk-window-cursor (text-view-get-window tv :text)) nil)
176             (when *active-tag*
177               (setf (text-tag-foreground *active-tag*) "blue"
178                     *active-tag* nil)))))))
179
180 (defun make-demo-text-view ()
181   (let ((tv (make-instance 'text-view :editable nil :cursor-visible nil :wrap-mode :word :pixels-below-lines 1 :left-margin 5 :right-margin 5)))
182     (setf (text-view-buffer tv)
183           (make-demo-text-buffer tv))
184     (connect-signal tv "motion-notify-event" #'tv-motion-notify)
185     tv))
186
187 (defun demo ()
188   (within-main-loop
189     (let-ui
190         (gtk-window
191          :var w
192          :title "Gtk+ demo for Lisp"
193          :window-position :center
194          :default-width 500
195          :default-height 500
196          (scrolled-window
197           :hscrollbar-policy :automatic
198           :vscrollbar-policy :automatic
199           (:expr (make-demo-text-view))))
200       (widget-show w))))
201
202 (defun test ()
203   "A simple test of 'on-expose' event"
204   (within-main-loop
205     (let ((window (make-instance 'gtk-window :type :toplevel))
206           (area (make-instance 'drawing-area))
207           x y)
208       (container-add window area)
209       (connect-signal window "destroy" (lambda (widget)
210                                          (declare (ignore widget))
211                                          (leave-gtk-main)))
212       (connect-signal area "motion-notify-event"
213                       (lambda (widget event)
214                         (declare (ignore widget))
215                         (setf x (event-motion-x event)
216                               y (event-motion-y event))
217                         (widget-queue-draw window)))
218       (connect-signal area "expose-event"
219                       (lambda (widget event)
220                         (declare (ignore widget event))
221                         (let* ((gdk-window (widget-window area))
222                                (gc (graphics-context-new gdk-window))
223                                (layout (widget-create-pango-layout area (format nil "X: ~F~%Y: ~F" x y))))
224                           (draw-layout gdk-window gc 0 0 layout)
225                           (setf (graphics-context-rgb-fg-color gc) (make-color :red 65535 :green 0 :blue 0))
226                           (multiple-value-bind (x y) (drawable-get-size gdk-window)
227                             (draw-line gdk-window gc 0 0 x y)))))
228       (connect-signal area "realize"
229                       (lambda (widget)
230                         (declare (ignore widget))
231                         (pushnew :pointer-motion-mask (gdk-window-events (widget-window area)))))
232       (connect-signal area "configure-event"
233                       (lambda (widget event)
234                         (declare (ignore widget event))
235                         (widget-queue-draw area)))
236       (widget-show window))))
237   
238 (defun test-entry ()
239   "Testing GtkTextEntry"
240   (within-main-loop
241     (let* ((window (make-instance 'gtk-window :type :toplevel :title "Testing entry" :border-width 10))
242            (box (make-instance 'v-box))
243            (entry (make-instance 'entry))
244            (button (make-instance 'button :label "OK"))
245            (text-buffer (make-instance 'text-buffer))
246            (text-view (make-instance 'text-view :buffer text-buffer))
247            (button-select (make-instance 'button :label "Select"))
248            (button-insert (make-instance 'button :label "Insert")))
249       (box-pack-start box (make-instance 'label :label "Enter <b>anything</b> you wish:" :use-markup t) :expand nil)
250       (box-pack-start box entry :expand nil)
251       (box-pack-start box button :expand nil)
252       (box-pack-start box button-select :expand nil)
253       (box-pack-start box button-insert :expand nil)
254       (let* ((w (make-instance 'scrolled-window)))
255         (box-pack-start box w)
256         (container-add w text-view))
257       (container-add window box)
258       (connect-signal window "destroy" (lambda (widget) (declare (ignore widget)) (leave-gtk-main)))
259       (connect-signal window "delete-event" (lambda (widget event)
260                                               (declare (ignore widget event))
261                                               (let ((dlg (make-instance 'message-dialog
262                                                                         :text "Are you sure?"
263                                                                         :buttons :yes-no)))
264                                                 (let ((response (dialog-run dlg)))
265                                                   (object-destroy dlg)
266                                                   (not (eq :yes response))))))
267       (connect-signal button "clicked" (lambda (button)
268                                          (declare (ignore button))
269                                          (setf (text-buffer-text text-buffer)
270                                                (format nil "~A~%~A" (text-buffer-text text-buffer) (entry-text entry))
271                                                (entry-text entry) "")))
272       (connect-signal button-select "clicked" (lambda (button)
273                                                 (declare (ignore button))
274                                                 (editable-select-region entry 5 10)))
275       (connect-signal button-insert "clicked" (lambda (button)
276                                                 (declare (ignore button))
277                                                 (editable-insert-text entry "hello" 2)))
278       (widget-show window))))
279
280 (defun table-packing ()
281   "Simple test of packing widgets into GtkTable"
282   (within-main-loop
283     (let* ((window (make-instance 'gtk-window :type :toplevel :title "Table packing" :border-width 20))
284            (table (make-instance 'table :n-rows 2 :n-columns 2 :homogeneous t))
285            (button-1 (make-instance 'button :label "Button 1"))
286            (button-2 (make-instance 'button :label "Button 2"))
287            (button-q (make-instance 'button :label "Quit")))
288       (container-add window table)
289       (table-attach table button-1 0 1 0 1)
290       (table-attach table button-2 1 2 0 1)
291       (table-attach table button-q 0 2 1 2)
292       (connect-signal window "destroy" (lambda (w) (declare (ignore w)) (leave-gtk-main)))
293       (connect-signal button-q "clicked" (lambda (b) (declare (ignore b)) (object-destroy window)))
294       (widget-show window))))
295
296 (defun test-pixbuf ()
297   "(not completed)"
298   (within-main-loop
299     (let* ((window (make-instance 'gtk-window :title "Test pixbuf" :width-request 600 :height-request 240))
300            (vbox (make-instance 'v-box))
301            (eventbox (make-instance 'event-box))
302            (vbox-1 (make-instance 'v-box)))
303       (container-add window vbox)
304       (box-pack-start vbox (make-instance 'label :text "Placing bg image" :font "Times New Roman Italic 10" :color "#00f" :height-request 40))
305       (connect-signal window "destroy" (lambda (w) (declare (ignore w)) (leave-gtk-main)))
306       (box-pack-start vbox eventbox)
307       (container-add eventbox vbox-1)
308       (box-pack-start vbox-1 (make-instance 'label :text "This is the eventbox"))
309       (box-pack-start vbox-1 (make-instance 'label :text "The green ball is the bg"))
310       (widget-show window))))
311
312 (defun test-image ()
313   "Using GtkImage with stock icon"
314   (within-main-loop
315     (let* ((window (make-instance 'gtk-window :title "Test images"))
316            (image (make-instance 'image :icon-name "applications-development" :icon-size 6)))
317       (container-add window image)
318       (connect-signal window "destroy" (lambda (w) (declare (ignore w)) (leave-gtk-main)))
319       (widget-show window))))
320
321 (defun test-progress-bar ()
322   "Testing progress-bar"
323   (within-main-loop
324     (let* ((window (make-instance 'gtk-window :title "Test progress bar"))
325            (v-box (make-instance 'v-box))
326            (p-bar (make-instance 'progress-bar :test "A process"))
327            (button-pulse (make-instance 'button :label "Pulse"))
328            (button-set (make-instance 'button :label "Set"))
329            (entry (make-instance 'entry)))
330       (connect-signal window "destroy" (lambda (w) (declare (ignore w)) (leave-gtk-main)))
331       (container-add window v-box)
332       (box-pack-start v-box p-bar)
333       (box-pack-start v-box button-pulse)
334       (box-pack-start v-box button-set)
335       (box-pack-start v-box entry)
336       (connect-signal button-pulse "clicked" (lambda (w) (declare (ignore w)) (progress-bar-pulse p-bar)))
337       (connect-signal button-set "clicked" (lambda (w)
338                                              (declare (ignore w))
339                                              (setf (progress-bar-fraction p-bar)
340                                                    (coerce (read-from-string (entry-text entry)) 'real))))
341       (widget-show window))))
342
343 (defun test-statusbar ()
344   "Test of GtkStatusbar"
345   (within-main-loop
346     (let* ((window (make-instance 'gtk-window :title "Text status bar"))
347            (v-box (make-instance 'v-box))
348            (h-box (make-instance 'h-box))
349            (label (make-instance 'label :label "Test of status bar" :xalign 0.5 :yalign 0.5))
350            (statusbar (make-instance 'statusbar :has-resize-grip t))
351            (button-push (make-instance 'button :label "Push"))
352            (button-pop (make-instance 'button :label "Pop"))
353            (entry (make-instance 'entry))
354            (icon (make-instance 'status-icon :icon-name "applications-development")))
355       (set-status-icon-tooltip icon "An icon from lisp program")
356       (connect-signal window "destroy" (lambda (w)
357                                          (declare (ignore w))
358                                          #+ (or) (setf (status-icon-visible icon) nil)
359                                          (leave-gtk-main)))
360       (connect-signal button-push "clicked" (lambda (b)
361                                               (declare (ignore b))
362                                               (statusbar-push statusbar "lisp-prog" (entry-text entry))))
363       (connect-signal button-pop "clicked" (lambda (b)
364                                              (declare (ignore b))
365                                              (statusbar-pop statusbar "lisp-prog")))
366       (connect-signal icon "activate" (lambda (i)
367                                         (declare (ignore i))
368                                         (let ((message-dialog (make-instance 'message-dialog
369                                                                              :buttons :ok
370                                                                              :text "You clicked on icon!")))
371                                           (dialog-run message-dialog)
372                                           (object-destroy message-dialog))))
373       (container-add window v-box)
374       (box-pack-start v-box h-box :expand nil)
375       (box-pack-start h-box entry)
376       (box-pack-start h-box button-push :expand nil)
377       (box-pack-start h-box button-pop :expand nil)
378       (box-pack-start v-box label)
379       (box-pack-start v-box statusbar :expand nil)
380       (widget-show window)
381       (setf (status-icon-screen icon) (gtk-window-screen window)))))
382
383 (defun test-scale-button ()
384   "Test of scale button with icons"
385   (within-main-loop
386     (let* ((window (make-instance 'gtk-window :type :toplevel :title "Testing scale button"))
387            (button (make-instance 'scale-button :icons (list "media-seek-backward" "media-seek-forward" "media-playback-stop" "media-playback-start") :adjustment (make-instance 'adjustment :lower -40 :upper 50 :value 20))))
388       (connect-signal window "destroy" (lambda (w) (declare (ignore w)) (leave-gtk-main)))
389       (container-add window button)
390       (widget-show window))))
391
392 (defun test-text-view ()
393   "Test of GtkTextView"
394   (within-main-loop
395     (let* ((window (make-instance 'gtk-window :type :toplevel :title "Testing text view" :width-request 400 :height-request 300))
396            (button (make-instance 'button :label "Do"))
397            (button-insert (make-instance 'button :label "Insert a button!"))
398            (bold-btn (make-instance 'button :label "Bold"))
399            (buffer (make-instance 'text-buffer :text "Some text buffer with some text inside"))
400            (v (make-instance 'text-view :buffer buffer :wrap-mode :word))
401            (box (make-instance 'v-box))
402            (scrolled (make-instance 'scrolled-window :hscrollbar-policy :automatic :vscrollbar-policy :automatic)))
403       (connect-signal window "destroy" (lambda (w) (declare (ignore w)) (leave-gtk-main)))
404       (connect-signal button "clicked" (lambda (b)
405                                          (declare (ignore b))
406                                          (multiple-value-bind (i1 i2) (text-buffer-get-selection-bounds buffer)
407                                            (when (and i1 i2)
408                                              (let* ((i1 i1) (i2 i2)
409                                                     (dialog (make-instance 'message-dialog :buttons :ok)))
410                                                (setf (message-dialog-text dialog)
411                                                      (format nil "selection: from (~A,~A) to (~A,~A)"
412                                                              (text-iter-line i1) (text-iter-line-offset i1)
413                                                              (text-iter-line i2) (text-iter-line-offset i2)))
414                                                (dialog-run dialog)
415                                                (object-destroy dialog))))))
416       (connect-signal bold-btn "clicked" (Lambda (b)
417                                            (declare (ignore b))
418                                            (multiple-value-bind (start end) (text-buffer-get-selection-bounds buffer)
419                                              (when (and start end)
420                                                (let* ((start start)
421                                                       (end end)
422                                                       (tag (text-tag-table-lookup (text-buffer-tag-table buffer) "bold")))
423                                                  (if (text-iter-has-tag start tag)
424                                                      (text-buffer-remove-tag buffer tag start end)
425                                                      (text-buffer-apply-tag buffer tag start end)))))))
426       (connect-signal button-insert "clicked" (lambda (b)
427                                                 (declare (ignore b))
428                                                 (let* ((iter (text-buffer-get-iter-at-mark buffer (text-buffer-get-mark buffer "insert")))
429                                                        (anchor (text-buffer-insert-child-anchor buffer iter))
430                                                        (button (make-instance 'button :label "A button!")))
431                                                   (widget-show button)
432                                                   (text-view-add-child-at-anchor v button anchor))))
433       (let ((tag (make-instance 'text-tag :name "bold" :weight 700)))
434         (text-tag-table-add (text-buffer-tag-table buffer) tag)
435         (connect-signal tag "event"
436                         (lambda (tag object event iter)
437                           (declare (ignore tag object iter))
438                           (when (eq (event-type event) :button-release)
439                             (let ((dlg (make-instance 'message-dialog :text "You clicked on bold text." :buttons :ok)))
440                               (dialog-run dlg)
441                               (object-destroy dlg))))))
442       (container-add window box)
443       (container-add scrolled v)
444       (box-pack-start box button :expand nil)
445       (box-pack-start box button-insert :expand nil)
446       (box-pack-start box bold-btn :expand nil)
447       (box-pack-start box scrolled)
448       (widget-show window))))
449
450 (defun demo-code-editor ()
451   "(unfinished)"
452   (within-main-loop
453     (let* ((window (make-instance 'gtk-window :type :toplevel :title "Code editor" :width-request 400 :height-request 400 :window-position :center))
454            (scrolled (make-instance 'scrolled-window :hscrollbar-policy :automatic :vscrollbar-policy :automatic))
455            (buffer (make-instance 'text-buffer))
456            (view (make-instance 'text-view :buffer buffer)))
457       (connect-signal window "destroy" (lambda (w) (declare (ignore w)) (leave-gtk-main)))
458       (container-add window scrolled)
459       (container-add scrolled view)
460       (widget-show window)
461       (connect-signal buffer "insert-text" (lambda (buffer location text len)
462                                              (let* ((buffer buffer)
463                                                     (location location))
464                                                (format t "~A~%" (list buffer location text len))))))))
465
466 (defstruct tvi title value)
467
468 (defun test-treeview-list ()
469   "Test of treeview with CL-GTK2-GTK:ARRAY-LIST-STORE"
470   (within-main-loop
471     (let* ((window (make-instance 'gtk-window :type :toplevel :title "Treeview (list)"))
472            (model (make-instance 'array-list-store))
473            (scroll (make-instance 'scrolled-window :hscrollbar-policy :automatic :vscrollbar-policy :automatic))
474            (tv (make-instance 'tree-view :headers-visible t :width-request 100 :height-request 400 :rules-hint t))
475            (h-box (make-instance 'h-box))
476            (v-box (make-instance 'v-box))
477            (title-entry (make-instance 'entry))
478            (value-entry (make-instance 'entry))
479            (button (make-instance 'button :label "Add")))
480       (store-add-column model "gchararray" #'tvi-title)
481       (store-add-column model "gint" #'tvi-value)
482       (store-add-item model (make-tvi :title "Monday" :value 1))
483       (store-add-item model (make-tvi :title "Tuesday" :value 2))
484       (store-add-item model (make-tvi :title "Wednesday" :value 3))
485       (store-add-item model (make-tvi :title "Thursday" :value 4))
486       (store-add-item model (make-tvi :title "Friday" :value 5))
487       (store-add-item model (make-tvi :title "Saturday" :value 6))
488       (store-add-item model (make-tvi :title "Sunday" :value 7))
489       (setf (tree-view-model tv) model (tree-view-tooltip-column tv) 0)
490       (gobject:connect-signal window "destroy" (lambda (w) (declare (ignore w)) (leave-gtk-main)))
491       (gobject:connect-signal button "clicked" (lambda (b)
492                                                  (declare (ignore b))
493                                                  (store-add-item model (make-tvi :title (entry-text title-entry)
494                                                                                  :value (or (parse-integer (entry-text value-entry) 
495                                                                                                            :junk-allowed t)
496                                                                                             0)))))
497       (connect-signal tv "row-activated" (lambda (tv path column)
498                                            (declare (ignore tv column))
499                                            (format t "You clicked on row ~A~%" (tree-path-indices path))))
500       (container-add window v-box)
501       (box-pack-start v-box h-box :expand nil)
502       (box-pack-start h-box title-entry :expand nil)
503       (box-pack-start h-box value-entry :expand nil)
504       (box-pack-start h-box button :expand nil)
505       (box-pack-start v-box scroll)
506       (container-add scroll tv)
507       (let ((column (make-instance 'tree-view-column :title "Title" :sort-column-id 0))
508             (renderer (make-instance 'cell-renderer-text :text "A text")))
509         (tree-view-column-pack-start column renderer)
510         (tree-view-column-add-attribute column renderer "text" 0)
511         (tree-view-append-column tv column)
512         (print (tree-view-column-tree-view column))
513         (print (tree-view-column-cell-renderers column)))
514       (let ((column (make-instance 'tree-view-column :title "Value"))
515             (renderer (make-instance 'cell-renderer-text :text "A text")))
516         (tree-view-column-pack-start column renderer)
517         (tree-view-column-add-attribute column renderer "text" 1)
518         (tree-view-append-column tv column)
519         (print (tree-view-column-tree-view column))
520         (print (tree-view-column-cell-renderers column)))
521       (widget-show window))))
522
523 (defun test-combo-box ()
524   "Testing GtkComboBox"
525   (within-main-loop
526     (let* ((window (make-instance 'gtk-window :type :toplevel :title "Treeview (list)"))
527            (model (make-instance 'array-list-store))
528            (combo-box (make-instance 'combo-box :model model))
529            (h-box (make-instance 'h-box))
530            (v-box (make-instance 'v-box))
531            (title-entry (make-instance 'entry))
532            (value-entry (make-instance 'entry))
533            (button (make-instance 'button :label "Add")))
534       (store-add-column model "gchararray" #'tvi-title)
535       (store-add-column model "gint" #'tvi-value)
536       (store-add-item model (make-tvi :title "Monday" :value 1))
537       (store-add-item model (make-tvi :title "Tuesday" :value 2))
538       (store-add-item model (make-tvi :title "Wednesday" :value 3))
539       (store-add-item model (make-tvi :title "Thursday" :value 4))
540       (store-add-item model (make-tvi :title "Friday" :value 5))
541       (store-add-item model (make-tvi :title "Saturday" :value 6))
542       (store-add-item model (make-tvi :title "Sunday" :value 7))
543       (gobject:connect-signal window "destroy" (lambda (w) (declare (ignore w)) (leave-gtk-main)))
544       (gobject:connect-signal button "clicked" (lambda (b)
545                                                  (declare (ignore b))
546                                                  (store-add-item model (make-tvi :title (entry-text title-entry)
547                                                                                  :value (or (parse-integer (entry-text value-entry) 
548                                                                                                            :junk-allowed t)
549                                                                                             0)))))
550       (connect-signal combo-box "changed" (lambda (c)
551                                             (declare (ignore c))
552                                             (format t "You clicked on row ~A~%" (combo-box-active combo-box))))
553       (container-add window v-box)
554       (box-pack-start v-box h-box :expand nil)
555       (box-pack-start h-box title-entry :expand nil)
556       (box-pack-start h-box value-entry :expand nil)
557       (box-pack-start h-box button :expand nil)
558       (box-pack-start v-box combo-box)
559       (let ((renderer (make-instance 'cell-renderer-text :text "A text")))
560         (cell-layout-pack-start combo-box renderer :expand t)
561         (cell-layout-add-attribute combo-box renderer "text" 0))
562       (let ((renderer (make-instance 'cell-renderer-text :text "A number")))
563         (cell-layout-pack-start combo-box renderer :expand nil)
564         (cell-layout-add-attribute combo-box renderer "text" 1))
565       (widget-show window))))
566
567 (defun test-ui-manager ()
568   "Testing GtkUIManager"
569   (within-main-loop
570     (let* ((window (make-instance 'gtk-window :type :toplevel :title "UI Manager" :default-width 200 :default-height 100 :window-position :center))
571            (ui-manager (make-instance 'ui-manager))
572            (print-confirmation t))
573       (ui-manager-add-ui-from-string ui-manager
574                                      "
575 <ui>
576   <toolbar action='toolbar1'>
577       <separator/>
578       <toolitem name='Left' action='justify-left'/>
579       <toolitem name='Center' action='justify-center'/>
580       <toolitem name='Right' action='justify-right'/>
581       <toolitem name='Zoom in' action='zoom-in' />
582       <toolitem name='print-confirm' action='print-confirm' />
583       <separator/>
584   </toolbar>
585 </ui>")
586       (gobject:connect-signal window "destroy" (lambda (w) (declare (ignore w)) (leave-gtk-main)))
587       (iter (with fn = (lambda (action) (when print-confirmation (format t "Action ~A with name ~A activated~%" action (action-name action)))))
588             (with action-group = (make-instance 'action-group :name "Actions"))
589             (finally (let ((a (make-instance 'toggle-action :name "print-confirm" :label "Print" :stock-id "gtk-print-report" :active t)))
590                        (connect-signal a "toggled" (lambda (action) (setf print-confirmation (toggle-action-active action))))
591                        (action-group-add-action action-group a))
592                      (ui-manager-insert-action-group ui-manager action-group 0))
593             (for (name stock-id) in '(("justify-left" "gtk-justify-left")
594                                       ("justify-center" "gtk-justify-center")
595                                       ("justify-right" "gtk-justify-right")
596                                       ("zoom-in" "gtk-zoom-in")))
597             (for action = (make-instance 'action :name name :stock-id stock-id))
598             (connect-signal action "activate" fn)
599             (action-group-add-action action-group action))
600       (let ((widget (ui-manager-widget ui-manager "/toolbar1")))
601         (when widget
602           (container-add window widget)))
603       (widget-show window))))
604
605 (defun test-color-button ()
606   "Test of GtkColorButton"
607   (within-main-loop
608     (let ((window (make-instance 'gtk-window :title "Color button" :type :toplevel :window-position :center :width-request 100 :height-request 100))
609           (button (make-instance 'color-button :title "Color button")))
610       (connect-signal window "destroy" (lambda (w) (declare (ignore w)) (leave-gtk-main)))
611       (connect-signal button "color-set" (lambda (b)
612                                            (declare (ignore b))
613                                            (format t "Chose color ~A~%" (color-button-color button))))
614       (container-add window button)
615       (widget-show window))))
616
617 (defun test-color-selection ()
618   "Test of GtkColorSelection"
619   (within-main-loop
620     (let ((window (make-instance 'gtk-window :title "Color selection" :type :toplevel :window-position :center))
621           (selection (make-instance 'color-selection :has-opacity-control t)))
622       (connect-signal window "destroy" (lambda (w) (declare (ignore w)) (leave-gtk-main)))
623       (connect-signal selection "color-changed" (lambda (s) (declare (ignore s)) (unless (color-selection-adjusting-p selection) (format t "color: ~A~%" (color-selection-current-color selection)))))
624       (container-add window selection)
625       (widget-show window))))
626
627 (defun test-file-chooser ()
628   "Test of GtkFileChooser"
629   (within-main-loop
630     (let ((window (make-instance 'gtk-window :title "file chooser" :type :toplevel :window-position :center :default-width 100 :default-height 100))
631           (v-box (make-instance 'v-box))
632           (button (make-instance 'file-chooser-button :action :open))
633           (b (make-instance 'button :label "Choose for save" :stock-id "gtk-save")))
634       (connect-signal window "destroy" (lambda (w) (declare (ignore w)) (leave-gtk-main)))
635       (connect-signal button "file-set" (lambda (b) (declare (ignore b)) (format t "File set: ~A~%" (file-chooser-filename button))))
636       (connect-signal b "clicked" (lambda (b)
637                                     (declare (ignore b))
638                                     (let ((d (make-instance 'file-chooser-dialog :action :save :title "Choose file to save")))
639                                       (dialog-add-button d "gtk-save" :accept)
640                                       (dialog-add-button d "gtk-cancel" :cancel)
641                                       (when (eq (dialog-run d) :accept)
642                                         (format t "saved to file ~A~%" (file-chooser-filename d)))
643                                       (object-destroy d))))
644       (container-add window v-box)
645       (box-pack-start v-box button)
646       (box-pack-start v-box b)
647       (widget-show window))))
648
649 (defun test-font-chooser ()
650   "GtkFontChooser"
651   (within-main-loop
652     (let ((window (make-instance 'gtk-window :title "fonts" :type :toplevel :window-position :center :default-width 100 :default-height 100))
653           (v-box (make-instance 'v-box))
654           (button (make-instance 'font-button :title "Choose font" :font-name "Sans 10")))
655       (connect-signal window "destroy" (lambda (w) (declare (ignore w)) (leave-gtk-main)))
656       (connect-signal button "font-set" (lambda (b) (declare (ignore b)) (format t "Chose font ~A~%" (font-button-font-name button))))
657       (container-add window v-box)
658       (box-pack-start v-box button)
659       (widget-show window))))
660
661 (defun test-notebook ()
662   "Test GtkNotebook"
663   (within-main-loop
664     (let ((window (make-instance 'gtk-window :title "Notebook" :type :toplevel :window-position :center :default-width 100 :default-height 100))
665           (expander (make-instance 'expander :expanded t :label "notebook"))
666           (notebook (make-instance 'notebook :enable-popup t)))
667       (connect-signal window "destroy" (lambda (w) (declare (ignore w)) (leave-gtk-main)))
668       (iter (for i from 0 to 5)
669             (for page = (make-instance 'label :label (format nil "Label for page ~A" i)))
670             (for tab-label = (make-instance 'label :label (format nil "Tab ~A" i)))
671             (for tab-button = (make-instance 'button
672                                              :image (make-instance 'image :stock "gtk-close" :icon-size 1)
673                                              :relief :none))
674             (connect-signal tab-button "clicked"
675                             (let ((page page))
676                               (lambda (button)
677                                 (declare (ignore button))
678                                 (format t "Removing page ~A~%" page)
679                                 (notebook-remove-page notebook page))))
680             (for tab-hbox = (make-instance 'h-box))
681             (box-pack-start tab-hbox tab-label)
682             (box-pack-start tab-hbox tab-button)
683             (widget-show tab-hbox)
684             (notebook-add-page notebook page tab-hbox))
685       (container-add window expander)
686       (container-add expander notebook)
687       (widget-show window))))
688
689 (defun calendar-detail (calendar year month day)
690   (declare (ignore calendar year month))
691   (when (= day 23)
692     "!"))
693
694 (defun test-calendar ()
695   "Test of GtkCalendar"
696   (within-main-loop
697     (let ((window (make-instance 'gtk-window :title "Calendar" :type :toplevel :window-position :center :default-width 100 :default-height 100))
698           (calendar (make-instance 'calendar :detail-function #'calendar-detail)))
699       (connect-signal window "destroy" (lambda (w) (declare (ignore w)) (leave-gtk-main)))
700       (connect-signal calendar "day-selected" (lambda (c) (declare (ignore c)) (format t "selected: year ~A month ~A day ~A~%"
701                                                                                        (calendar-year calendar)
702                                                                                        (calendar-month calendar)
703                                                                                        (calendar-day calendar))))
704       (container-add window calendar)
705       (widget-show window))))
706
707 (defun test-box-child-property ()
708   "Test of child-property usage"
709   (within-main-loop
710     (let ((window (make-instance 'gtk-window :title "Text box child property" :type :toplevel :window-position :center :width-request 200 :height-request 200))
711           (box (make-instance 'h-box))
712           (button (make-instance 'toggle-button :active t :label "Expand")))
713       (connect-signal window "destroy" (lambda (w) (declare (ignore w)) (leave-gtk-main)))
714       (connect-signal button "toggled" (lambda (b) (declare (ignore b)) (setf (box-child-expand box button) (toggle-button-active button))))
715       (container-add window box)
716       (box-pack-start box button)
717       (widget-show window))))
718
719 (defun test-builder ()
720   "Test of GtkBuilder"
721   (within-main-loop
722     (let ((builder (make-instance 'builder)))
723       (builder-add-from-file builder (namestring (merge-pathnames "demo/demo1.ui" *src-location*)))
724       (let ((text-view (builder-get-object builder "textview1"))
725             (c 0))
726         (builder-connect-signals-simple builder `(("toolbutton1_clicked_cb" ,(lambda (b)
727                                                                                      (declare (ignore b))
728                                                                                      #+nil(print (current-event))
729                                                                                      (setf (text-buffer-text (text-view-buffer text-view))
730                                                                                            (format nil "Clicked ~A times~%" (incf c)))
731                                                                                      (statusbar-pop (builder-get-object builder "statusbar1")
732                                                                                                     "times")
733                                                                                      (statusbar-push (builder-get-object builder "statusbar1")
734                                                                                                      "times"
735                                                                                                      (format nil "~A times" c))))
736                                                   ("quit_cb" ,(lambda (&rest args)
737                                                                       (print args)
738                                                                       (object-destroy (builder-get-object builder "window1"))))
739                                                   ("about_cb" ,(lambda (&rest args)
740                                                                        (print args)
741                                                                        (let ((d (make-instance 'about-dialog
742                                                                                                :program-name "GtkBuilder text"
743                                                                                                :version "0.00001"
744                                                                                                :authors '("Dmitry Kalyanov")
745                                                                                                :logo-icon-name "gtk-apply")))
746                                                                          (dialog-run d)
747                                                                          (object-destroy d)))))))
748       (connect-signal (builder-get-object builder "window1") "destroy" (lambda (w) (declare (ignore w)) (leave-gtk-main)))
749       (statusbar-push (builder-get-object builder "statusbar1") "times" "0 times")
750       (widget-show (builder-get-object builder "window1")))))
751
752 (defun read-text-file (file-name)
753   (with-output-to-string (str)
754     (with-open-file (file file-name)
755       (loop
756          for line = (read-line file nil nil)
757          while line
758          do (fresh-line str)
759          do (write-string line str)))))
760
761 (defun demo-text-editor ()
762   "More advanced example: text editor with ability to evaluate lisp expressions"
763   (within-main-loop
764     (let* ((builder (let ((builder (make-instance 'builder)))
765                       (builder-add-from-file builder (namestring (merge-pathnames "demo/text-editor.ui" *src-location*)))
766                       builder))
767            (window (builder-get-object builder "window1"))
768            (text-view (builder-get-object builder "textview1"))
769            (statusbar (builder-get-object builder "statusbar1"))
770            (file-name nil)
771            (modified-p t))
772       (statusbar-push statusbar "filename" "Untitled *")
773       (labels ((set-properties ()
774                  (statusbar-pop statusbar "filename")
775                  (statusbar-push statusbar "filename" (format nil "~A~:[~; *~]" (or file-name "Untitled") modified-p)))
776                (new (&rest args) (declare (ignore args))
777                     (setf file-name nil
778                           modified-p t
779                           (text-buffer-text (text-view-buffer text-view)) "")
780                     (set-properties))
781                (cb-open (&rest args) (declare (ignore args))
782                         (let ((d (make-instance 'file-chooser-dialog :action :open :title "Open file")))
783                           (when file-name (setf (file-chooser-filename d) file-name))
784                           (dialog-add-button d "gtk-open" :accept)
785                           (dialog-add-button d "gtk-cancel" :cancel)
786                           (when (eq :accept (dialog-run d))
787                             (setf file-name (file-chooser-filename d)
788                                   (text-buffer-text (text-view-buffer text-view)) (read-text-file file-name)
789                                   modified-p nil)
790                             (set-properties))
791                           (object-destroy d)))
792                (save (&rest args) (declare (ignore args))
793                      (if file-name
794                          (progn
795                            (with-open-file (file file-name :direction :output :if-exists :supersede)
796                              (write-string (text-buffer-text (text-view-buffer text-view)) file))
797                            (setf modified-p nil)
798                            (set-properties))
799                          (save-as)))
800                (save-as (&rest args) (declare (ignore args))
801                         (let ((d (make-instance 'file-chooser-dialog :action :save :title "Save file")))
802                           (when file-name (setf (file-chooser-filename d) file-name))
803                           (dialog-add-button d "gtk-save" :accept)
804                           (dialog-add-button d "gtk-cancel" :cancel)
805                           (if (eq :accept (dialog-run d))
806                               (progn
807                                 (setf file-name (file-chooser-filename d))
808                                 (object-destroy d)
809                                 (save))
810                               (object-destroy d))))
811                (cut (&rest args) (declare (ignore args))
812                     (text-buffer-cut-clipboard (text-view-buffer text-view) (get-clipboard "CLIPBOARD") t))
813                (copy (&rest args) (declare (ignore args))
814                      (text-buffer-copy-clipboard (text-view-buffer text-view) (get-clipboard "CLIPBOARD")))
815                (paste (&rest args) (declare (ignore args))
816                       (text-buffer-paste-clipboard (text-view-buffer text-view) (get-clipboard "CLIPBOARD")))
817                (cb-delete (&rest args) (declare (ignore args))
818                           (let ((buffer (text-view-buffer text-view)))
819                             (multiple-value-bind (i1 i2) (text-buffer-get-selection-bounds buffer)
820                               (when (and i1 i2)
821                                 (text-buffer-delete buffer i1 i2)))))
822                (about (&rest args) (declare (ignore args))
823                       (let ((d (make-instance 'about-dialog
824                                               :program-name "Lisp Gtk+ Binding Demo Text Editor"
825                                               :version (format nil "0.0.0.1 ~A" #\GREEK_SMALL_LETTER_ALPHA)
826                                               :authors '("Kalyanov Dmitry")
827                                               :license "Public Domain"
828                                               :logo-icon-name "accessories-text-editor")))
829                         (dialog-run d)
830                         (object-destroy d)))
831                (quit (&rest args) (declare (ignore args)) (object-destroy window))
832                (cb-eval (&rest args) (declare (ignore args))
833                         (let ((buffer (text-view-buffer text-view)))
834                           (multiple-value-bind (i1 i2) (text-buffer-get-selection-bounds buffer)
835                             (when (and i1 i2)
836                               (with-gtk-message-error-handler
837                                 (let* ((text (text-buffer-slice buffer i1 i2))
838                                        (value (eval (read-from-string text)))
839                                        (value-str (format nil "~A" value))
840                                        (pos (max (text-iter-offset i1) (text-iter-offset i2))))
841                                   (text-buffer-insert buffer " => " :position (text-buffer-get-iter-at-offset buffer pos))
842                                   (incf pos (length " => "))
843                                   (text-buffer-insert buffer value-str :position (text-buffer-get-iter-at-offset buffer pos)))))))))
844         (builder-connect-signals-simple builder `(("new" ,#'new)
845                                                   ("open" ,#'cb-open)
846                                                   ("save" ,#'save)
847                                                   ("save-as" ,#'save-as)
848                                                   ("cut" ,#'cut)
849                                                   ("copy" ,#'copy)
850                                                   ("paste" ,#'paste)
851                                                   ("delete" ,#'cb-delete)
852                                                   ("about" ,#'about)
853                                                   ("quit" ,#'quit)
854                                                   ("eval" ,#'cb-eval)))
855         (connect-signal window "destroy" (lambda (w) (declare (ignore w)) (leave-gtk-main)))
856         (connect-signal (text-view-buffer text-view) "changed" (lambda (b) (declare (ignore b)) (setf modified-p t) (set-properties)))
857         (widget-show window)))))
858
859 (defun demo-class-browser ()
860   "Show slots of a given class"
861   (let ((output *standard-output*))
862     (with-main-loop
863       (let* ((window (make-instance 'gtk-window
864                                     :window-position :center
865                                     :title "Class Browser"
866                                     :default-width 400
867                                     :default-height 600))
868              (search-entry (make-instance 'entry))
869              (search-button (make-instance 'button :label "Search"))
870              (scroll (make-instance 'scrolled-window
871                                     :hscrollbar-policy :automatic
872                                     :vscrollbar-policy :automatic))
873              (slots-model (make-instance 'array-list-store))
874              (slots-list (make-instance 'tree-view :model slots-model)))
875         (let ((v-box (make-instance 'v-box))
876               (search-box (make-instance 'h-box)))
877           (container-add window v-box)
878           (box-pack-start v-box search-box :expand nil)
879           (box-pack-start search-box search-entry)
880           (box-pack-start search-box search-button :expand nil)
881           (box-pack-start v-box scroll)
882           (container-add scroll slots-list))
883         (store-add-column slots-model "gchararray"
884                           (lambda (slot)
885                             (format nil "~S" (closer-mop:slot-definition-name slot))))
886         (let ((col (make-instance 'tree-view-column :title "Slot name"))
887               (cr (make-instance 'cell-renderer-text)))
888           (tree-view-column-pack-start col cr)
889           (tree-view-column-add-attribute col cr "text" 0)
890           (tree-view-append-column slots-list col))
891         (labels ((display-class-slots (class)
892                    (format output "Displaying ~A~%" class)
893                    (loop
894                       repeat (store-items-count slots-model)
895                       do (store-remove-item slots-model (store-item slots-model 0)))
896                    (closer-mop:finalize-inheritance class)
897                    (loop
898                       for slot in (closer-mop:class-slots class)
899                       do (store-add-item slots-model slot)))
900                  (on-search-clicked (button)
901                    (declare (ignore button))
902                    (with-gtk-message-error-handler
903                      (let* ((class-name (read-from-string (entry-text search-entry)))
904                             (class (find-class class-name)))
905                        (display-class-slots class)))))
906           (connect-signal search-button "clicked" #'on-search-clicked))
907         (widget-show window)))))
908
909 (defun make-tree-from-sexp (l)
910   (setf l (if (listp l) l (list l)))
911   (let ((node (make-tree-node :item (make-tvi :title (format nil "~S" (first l))
912                                               :value (format nil "~S" (class-of (first l)))))))
913     (iter (for child in (rest l))
914           (tree-node-insert-at node (make-tree-from-sexp child) (length (tree-node-children node))))
915     node))
916
917 (defun demo-treeview-tree ()
918   "Advanced demo: show s-expression tree structure"
919   (within-main-loop
920     (let* ((window (make-instance 'gtk-window :type :toplevel :title "Treeview (tree)"))
921            (model (make-instance 'tree-lisp-store))
922            (scroll (make-instance 'scrolled-window :hscrollbar-policy :automatic :vscrollbar-policy :automatic))
923            (tree-view (make-instance 'tree-view :headers-visible t :width-request 300 :height-request 400 :rules-hint t))
924            (h-box (make-instance 'h-box))
925            (v-box (make-instance 'v-box))
926            (entry (make-instance 'entry))
927            (button (make-instance 'button :label "Display")))
928       (tree-lisp-store-add-column model "gchararray" #'tvi-title)
929       (tree-lisp-store-add-column model "gchararray" #'tvi-value)
930       (tree-node-insert-at (tree-lisp-store-root model)
931                            (make-tree-from-sexp '(lambda (object &rest initargs &key &allow-other-keys)
932                                                   (* 1 2)
933                                                   (- 3 4)))
934                            0)
935       (setf (tree-view-model tree-view) model
936             (tree-view-tooltip-column tree-view) 0)
937       (connect-signal tree-view "row-activated" (lambda (tv path column)
938                                                   (declare (ignore tv column))
939                                                   (format t "You clicked on row ~A~%" (tree-path-indices path))))
940       (connect-signal button "clicked" (lambda (b)
941                                          (declare (ignore b))
942                                          (let ((object (read-from-string (entry-text entry))))
943                                            (tree-node-remove-at (tree-lisp-store-root model) 0)
944                                            (tree-node-insert-at (tree-lisp-store-root model)
945                                                                 (make-tree-from-sexp object)
946                                                                 0))))
947       (container-add window v-box)
948       (box-pack-start v-box h-box :expand nil)
949       (box-pack-start h-box entry)
950       (box-pack-start h-box button :expand nil)
951       (box-pack-start v-box scroll)
952       (container-add scroll tree-view)
953       (let ((column (make-instance 'tree-view-column :title "Value" :sort-column-id 0))
954             (renderer (make-instance 'cell-renderer-text :text "A text")))
955         (tree-view-column-pack-start column renderer)
956         (tree-view-column-add-attribute column renderer "text" 0)
957         (tree-view-append-column tree-view column)
958         (print (tree-view-column-tree-view column))
959         (print (tree-view-column-cell-renderers column)))
960       (let ((column (make-instance 'tree-view-column :title "Type"))
961             (renderer (make-instance 'cell-renderer-text :text "A text")))
962         (tree-view-column-pack-start column renderer)
963         (tree-view-column-add-attribute column renderer "text" 1)
964         (tree-view-append-column tree-view column)
965         (print (tree-view-column-tree-view column))
966         (print (tree-view-column-cell-renderers column)))
967       (widget-show window))))
968
969 (defclass custom-window (gtk-window)
970   ((label :initform (make-instance 'label :label "A label text") :reader custom-window-label)
971    (button :initform (make-instance 'button :label "Click me!") :reader custom-window-button))
972   (:metaclass gobject-class)
973   (:default-initargs :title "Custom window with default initargs" :default-width 320 :default-height 240))
974
975 (defun custom-window-label-text (w)
976   (label-label (custom-window-label w)))
977
978 (defun (setf custom-window-label-text) (new-value w)
979   (setf (label-label (custom-window-label w)) new-value))
980
981 (defmethod initialize-instance :after ((w custom-window) &key &allow-other-keys)
982   (let ((box (make-instance 'v-box)))
983     (box-pack-start box (custom-window-label w))
984     (box-pack-start box (custom-window-button w) :expand nil)
985     (container-add w box))
986   (connect-signal (custom-window-button w) "clicked" (lambda (b)
987                                                        (declare (ignore b))
988                                                        (custom-window-button-clicked w))))
989
990 (defun custom-window-button-clicked (w)
991   (setf (custom-window-label-text w)
992         (format nil "Now is: ~A~%" (get-internal-run-time))))
993
994 (defun test-custom-window ()
995   "Simple test of non-GObject subclass of GtkWindow"
996   (within-main-loop
997     (let ((w (make-instance 'custom-window)))
998       (widget-show w))))
999
1000 (defun test-assistant ()
1001   "Simple test of GtkAssistant wizard"
1002   (let ((output *standard-output*))
1003     (within-main-loop
1004       (let ((d (make-instance 'assistant :title "Username wizard"))
1005             (p-1 (make-instance 'h-box))
1006             (entry (make-instance 'entry))
1007             (p-2 (make-instance 'label :label "Click Apply to close this wizard")))
1008         (box-pack-start p-1 (make-instance 'label :label "Enter your name:") :expand nil)
1009         (box-pack-start p-1 entry)
1010         (assistant-append-page d p-1)
1011         (assistant-append-page d p-2)
1012         (setf (assistant-child-title d p-1) "Username wizard"
1013               (assistant-child-title d p-2) "Username wizard"
1014               (assistant-child-complete d p-1) nil
1015               (assistant-child-complete d p-2) t
1016               (assistant-child-page-type d p-1) :intro
1017               (assistant-child-page-type d p-2) :confirm
1018               (assistant-forward-page-function d) (lambda (i)
1019                                                     (format output "(assistant-forward-page-function ~A)~%" i)
1020                                                     (ecase i
1021                                                       (0 1)
1022                                                       (1 -1))))
1023         (connect-signal entry "notify::text" (lambda (object pspec)
1024                                                (declare (ignore object pspec))
1025                                                (setf (assistant-child-complete d p-1)
1026                                                      (plusp (length (entry-text entry))))))
1027         (let ((w (make-instance 'label :label "A label in action area")))
1028           (widget-show w)
1029           (assistant-add-action-widget d w))
1030         (connect-signal d "cancel" (lambda (assistant)
1031                                      (declare (ignore assistant))
1032                                      (object-destroy d)
1033                                      (format output "Canceled~%")))
1034         (connect-signal d "close" (lambda (assistant)
1035                                     (declare (ignore assistant))
1036                                     (object-destroy d)
1037                                     (format output "Thank you, ~A~%" (entry-text entry))))
1038         (connect-signal d "prepare" (lambda (assistant page-widget)
1039                                       (declare (ignore assistant page-widget))
1040                                       (format output "Assistant ~A has ~A pages and is on ~Ath page~%"
1041                                               d (assistant-n-pages d) (assistant-current-page d))))
1042         (widget-show d)))))
1043
1044 (defun test-entry-completion ()
1045   "Not working example of GtkEntryCompletion"
1046   (within-main-loop
1047     (let* ((w (make-instance 'gtk-window))
1048            (model (make-instance 'tree-lisp-store)))
1049       (tree-lisp-store-add-column model "gchararray" #'identity)
1050       (tree-node-insert-at (tree-lisp-store-root model) (make-tree-node :item "Monday") 0)
1051       (tree-node-insert-at (tree-lisp-store-root model) (make-tree-node :item "Tuesday") 0)
1052       (tree-node-insert-at (tree-lisp-store-root model) (make-tree-node :item "Wednesday") 0)
1053       (tree-node-insert-at (tree-lisp-store-root model) (make-tree-node :item "Thursday") 0)
1054       (tree-node-insert-at (tree-lisp-store-root model) (make-tree-node :item "Friday") 0)
1055       (tree-node-insert-at (tree-lisp-store-root model) (make-tree-node :item "Saturday") 0)
1056       (tree-node-insert-at (tree-lisp-store-root model) (make-tree-node :item "Sunday") 0)
1057       (let* ((completion (make-instance 'entry-completion :model model :text-column 0))
1058              (e (make-instance 'entry :completion completion)))
1059         (setf (entry-completion-text-column completion) 0)
1060         (container-add w e))
1061       (widget-show w))))
1062
1063 (defun test-ui-markup ()
1064   (within-main-loop
1065     (let ((label (make-instance 'label :label "Hello!")))
1066       (let-ui (gtk-window :type :toplevel
1067                           :position :center
1068                           :title "Hello, world!"
1069                           :default-width 300
1070                           :default-height 400
1071                           :var w
1072                           (v-box
1073                            (:expr label) :expand nil
1074                            (scrolled-window
1075                             :hscrollbar-policy :automatic
1076                             :vscrollbar-policy :automatic
1077                             :shadow-type :etched-in
1078                             (text-view :var tv))
1079                            (h-box
1080                             (label :label "Insert:") :expand nil
1081                             (entry :var entry)
1082                             (button :label "gtk-ok" :use-stock t :var btn) :expand nil)
1083                            :expand nil
1084                            (label :label "Table packing")
1085                            :expand nil
1086                            (table
1087                             :n-columns 2
1088                             :n-rows 2
1089                             (label :label "2 x 1") :left 0 :right 2 :top 0 :bottom 1
1090                             (label :label "1 x 1") :left 0 :right 1 :top 1 :bottom 2
1091                             (label :label "1 x 1") :left 1 :right 2 :top 1 :bottom 2)))
1092         (connect-signal btn "clicked"
1093                         (lambda (b)
1094                           (declare (ignore b))
1095                           (text-buffer-insert (text-view-buffer tv)
1096                                               (entry-text entry))))
1097         (widget-show w)))))
1098
1099 (defun test-list-store ()
1100   "Demonstrates usage of list store"
1101   (within-main-loop
1102     (let-ui (gtk-window
1103              :type :toplevel
1104              :title "GtkListStore"
1105              :default-width 600
1106              :default-height 400
1107              :var w
1108              (v-box
1109               (label :label "A GtkListStore") :expand nil
1110               (scrolled-window
1111                :hscrollbar-policy :automatic
1112                :vscrollbar-policy :automatic
1113                (tree-view :var tv))))
1114       (let ((l (make-instance 'list-store :column-types '("gint" "gchararray"))))
1115         (iter (for i from 0 below 100)
1116               (for n = (random 10000000))
1117               (for s = (format nil "~R" n))
1118               (list-store-insert-with-values l i n s))
1119         (setf (tree-view-model tv) l)
1120         (let ((column (make-instance 'tree-view-column :title "Number" :sort-column-id 0))
1121               (renderer (make-instance 'cell-renderer-text :text "A text")))
1122           (tree-view-column-pack-start column renderer)
1123           (tree-view-column-add-attribute column renderer "text" 0)
1124           (tree-view-append-column tv column))
1125         (let ((column (make-instance 'tree-view-column :title "As string" :sort-column-id 1))
1126               (renderer (make-instance 'cell-renderer-text :text "A text")))
1127           (tree-view-column-pack-start column renderer)
1128           (tree-view-column-add-attribute column renderer "text" 1)
1129           (tree-view-append-column tv column))
1130         (connect-signal tv "row-activated"
1131                         (lambda (w path column)
1132                           (declare (ignore w column))
1133                           (let* ((iter (tree-model-iter-by-path l path))
1134                                  (n (tree-model-value l iter 0))
1135                                  (dialog (make-instance 'message-dialog
1136                                                         :title "Clicked"
1137                                                         :text (format nil "Number ~A was clicked" n)
1138                                                         :buttons :ok)))
1139                             (dialog-run dialog)
1140                             (object-destroy dialog)))))
1141       (widget-show w))))
1142
1143 (defun test-tree-store ()
1144   "Demonstrates usage of tree store"
1145   (within-main-loop
1146     (let-ui (gtk-window
1147              :type :toplevel
1148              :title "GtkListStore"
1149              :default-width 600
1150              :default-height 400
1151              :var w
1152              (v-box
1153               (label :label "A GtkListStore") :expand nil
1154               (scrolled-window
1155                :hscrollbar-policy :automatic
1156                :vscrollbar-policy :automatic
1157                (tree-view :var tv))))
1158       (let ((l (make-instance 'tree-store :column-types '("gint" "gchararray"))))
1159         (iter (for i from 0 below 100)
1160               (for n = (random 10000000))
1161               (for s = (format nil "~R" n))
1162               (for it = (tree-store-insert-with-values l nil i n s))
1163               (iter (for j from 0 below 10)
1164                     (for n2 = (random 10000000))
1165                     (for s2 = (format nil "~R" n2))
1166                     (tree-store-insert-with-values l it j n2 s2)))
1167         (setf (tree-view-model tv) l)
1168         (let ((column (make-instance 'tree-view-column :title "Number" :sort-column-id 0))
1169               (renderer (make-instance 'cell-renderer-text :text "A text")))
1170           (tree-view-column-pack-start column renderer)
1171           (tree-view-column-add-attribute column renderer "text" 0)
1172           (tree-view-append-column tv column))
1173         (let ((column (make-instance 'tree-view-column :title "As string" :sort-column-id 1))
1174               (renderer (make-instance 'cell-renderer-text :text "A text")))
1175           (tree-view-column-pack-start column renderer)
1176           (tree-view-column-add-attribute column renderer "text" 1)
1177           (tree-view-append-column tv column))
1178         (connect-signal tv "row-activated"
1179                         (lambda (w path column)
1180                           (declare (ignore w column))
1181                           (let* ((iter (tree-model-iter-by-path l path))
1182                                  (n (tree-model-value l iter 0))
1183                                  (dialog (make-instance 'message-dialog
1184                                                         :title "Clicked"
1185                                                         :text (format nil "Number ~A was clicked" n)
1186                                                         :buttons :ok)))
1187                             (dialog-run dialog)
1188                             (object-destroy dialog)))))
1189       (widget-show w))))
1190
1191 (defun test-gdk-expose (gdk-window)
1192   (let* ((gc (graphics-context-new gdk-window)))
1193     (multiple-value-bind (w h) (drawable-get-size gdk-window)
1194       (setf (graphics-context-rgb-bg-color gc) (make-color :red 0 :green 0 :blue 0))
1195       (draw-polygon gdk-window gc t (list (make-point :x 0 :y 0)
1196                                           (make-point :x (truncate w 2) :y 0)
1197                                           (make-point :x w :y (truncate h 2))
1198                                           (make-point :x w :y h)
1199                                           (make-point :x (truncate w 2) :y h)
1200                                           (make-point :x 0 :y (truncate h 2))))
1201       (setf (graphics-context-rgb-fg-color gc) (make-color :red 65535 :green 0 :blue 0))
1202       (draw-point gdk-window gc 20 10)
1203       (setf (graphics-context-rgb-fg-color gc) (make-color :red 0 :green 65535 :blue 0))
1204       (draw-points gdk-window gc (list (make-point :x 15 :y 20) (make-point :x 35 :y 40)))
1205       (setf (graphics-context-rgb-fg-color gc) (make-color :red 0 :green 0 :blue 65535))
1206       (draw-line gdk-window gc 60 30 40 50)
1207       (setf (graphics-context-rgb-fg-color gc) (make-color :red 65535 :green 65535 :blue 0))
1208       (draw-lines gdk-window gc (list (make-point :x 10 :y 30) (make-point :x 15 :y 40)
1209                                       (make-point :x 15 :y 50) (make-point :x 10 :y 56)))
1210       (setf (graphics-context-rgb-fg-color gc) (make-color :red 0 :green 65535 :blue 65535))
1211       (draw-segments gdk-window gc (list (make-segment :x1 35 :y1 35 :x2 55 :y2 35)
1212                                          (make-segment :x1 65 :y1 35 :x2 43 :y2 17)))
1213       (setf (graphics-context-rgb-fg-color gc) (make-color :red 65535 :green 0 :blue 65535)
1214             (graphics-context-rgb-bg-color gc) (make-color :red 32767 :green 0 :blue 32767))
1215       (draw-arc gdk-window gc nil 70 30 75 50 (* 64 75) (* 64 200))
1216       (draw-polygon gdk-window gc nil (list (make-point :x 20 :y 40)
1217                                             (make-point :x 30 :y 50)
1218                                             (make-point :x 40 :y 70)
1219                                             (make-point :x 30 :y 80)
1220                                             (make-point :x 10 :y 55)))
1221       (setf (graphics-context-rgb-fg-color gc) (make-color :red 16384 :green 16384 :blue 65535))
1222       (draw-trapezoids gdk-window gc (list (make-trapezoid :y1 50.0d0 :y2 70.0d0
1223                                                            :x11 30.0d0 :x12 45.0d0
1224                                                            :x21 70.0d0 :x22 50.0d0))))))
1225
1226 (defun test-gdk ()
1227   "Test various gdk primitives"
1228   (within-main-loop
1229     (let ((window (make-instance 'gtk-window :type :toplevel :app-paintable t)))
1230       (connect-signal window "destroy" (lambda (widget)
1231                                          (declare (ignore widget))
1232                                          (leave-gtk-main)))
1233       (connect-signal window "destroy" (lambda (widget)
1234                                          (declare (ignore widget))
1235                                          (leave-gtk-main)))
1236       (connect-signal window "expose-event"
1237                       (lambda (widget event)
1238                         (declare (ignore widget event))
1239                         (test-gdk-expose (widget-window window))))
1240       (connect-signal window "configure-event"
1241                       (lambda (widget event)
1242                         (declare (ignore widget event))
1243                         (widget-queue-draw window)))
1244       (widget-show window)
1245       (push :pointer-motion-mask (gdk-window-events (widget-window window))))))