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