@c -----------------------------------------------------------------------------
@c File     : gtk-misc-widgets.texi
@c License  : GNU General Public License (GPL)
@c Language : English
@c Author   : Dieter Kaiser
@c Date     : 15.02.2012
@c Revision : 11.09.2013
@c
@c Copyright (C) 2012, 2013 by Dieter Kaiser
@c -----------------------------------------------------------------------------

@menu
* Arrows::
* Calendar::
* Event Box::
* Text Entries::
* Spin Buttons::
* Combo Box::
@end menu

@c -----------------------------------------------------------------------------
@node Arrows, Calendar, Top, Top
@section Arrows

@findex GtkArrow
@findex gtk-arrow
@findex arrow, gtk-arrow

@ifnotinfo
@float Figure, figure-arrows
@caption{Arrows}
@center @image{figures/arrow-buttons, 233pt}
@end float
@end ifnotinfo

@findex gtk-arrow-new
@findex arrow-new, gtk-arrow-new

@findex gtk-arrow-set
@findex arrow-set, gtk-arrow-set

@findex GtkArrowType
@findex gtk-arrow-type
@findex arrow-type, gtk-arrow-type

The arrow widget draws an arrowhead, facing in a number of possible directions
and having a number of possible styles.  It can be very useful when placed on a
button.  Like the label widget, the arrow widget emits no signals.
There are only two functions for manipulating an arrow widget
@xurl{gtk-arrow-new} and @xurl{gtk-arrow-set}.  The first function creates a new
arrow widget with the indicated type and appearance.  The second function allows
these values to be altered retrospectively.  The type of an arrow can be one of
of the values of the enumeration type @xurl{gtk-arrow-type}. Possible values are
@code{:up}, @code{:down}, @code{:left} and @code{:right}.  These values
obviously indicate the direction in which the arrow will point.

@findex GtkShadowType
@findex gtk-shadow-type
@findex shadow-type, gtk-shadow-type

The shadow type argument is of the enumeration type @xurl{gtk-shadow-type} and
may take one of the the values @code{:none}, @code{:in}, @code{:etched-in} and
@code{:etched-out}.

@findex arrow-scaling

The amount of space used by the arrow is controlled by the style property
@code{arrow-scaling}.  The style property @code{arrow-scaling} takes values of
type @code{double} in a range of @code{[0,1]}.  The default value is @code{0.7}.

@findex gtk-widget-tooltip-text
@findex widget-tooltip-text, gtk-widget-tooltip-text

@ref{example-arrows} shows a brief example to illustrate the use of arrows in
buttons.  In addition, this example introduces the function
@xurl{gtk-widget-tooltip-text}, which attaches a tooltip to the button
widget.  The tooltip pops up, when the mouse is over the button.

@float Example, example-arrows
@caption{Buttons with Arrows}
@end float
@verbatim
(defun create-arrow-button (arrow-type shadow-type)
  (let (;; Create a button
        (button (make-instance 'gtk-button
                               ;; Add a small margin around the button
                               :margin 3
                               ;; Make big buttons of size 75 x 75
                               :width-request 75
                               :height-request 75)))
    ;; Add an arrow to the button
    (gtk-container-add button
                       (make-instance 'gtk-arrow
                                      :arrow-type arrow-type
                                      :shadow-type shadow-type))
    ;; Add a tooltip to the button
    (setf (gtk-widget-tooltip-text button)
          (format nil "Arrow of type ~A" (symbol-name arrow-type)))
    button))

(defun example-arrows ()
  (within-main-loop
    (let ((;; Create the main window
           window (make-instance 'gtk-window
                                 :type :toplevel
                                 :title "Example Arrow Buttons"
                                 :default-width 275
                                 :default-height 125
                                 :border-width 12))
          ;; Create a grid for the buttons
          (grid (make-instance 'gtk-grid
                               :orientation :horizontal
                               :column-homogeneous t)))
      ;; Connect a signal handler to the window
      (g-signal-connect window "destroy"
                        (lambda (widget)
                          (declare (ignore widget))
                          (leave-gtk-main)))
      ;; Create buttons with an arrow and add the buttons to the grid
      (gtk-container-add grid (create-arrow-button :up :in))
      (gtk-container-add grid (create-arrow-button :down :out))
      (gtk-container-add grid (create-arrow-button :left :etched-in))
      (gtk-container-add grid (create-arrow-button :right :etched-out))
      ;; Add the grid to the window
      (gtk-container-add window grid)
      ;; Show the window
      (gtk-widget-show-all window))))
@end verbatim

@c -----------------------------------------------------------------------------
@node Calendar, Event Box, Arrows, Top
@section Calendar

@findex GtkCalendar
@findex gtk-calendar
@findex calendar, gtk-calendar

@ifnotinfo
@float Figure, figure-calendar
@caption{Calendar}
@center @image{figures/calendar, 168pt}
@end float
@end ifnotinfo

@findex gtk-calendar-new
@findex calendar-new, gtk-calendar-new

The @xurl{gtk-calendar} widget displays a Gregorian calendar, one month at
a time.  It can be created with the function @xurl{gtk-calendar-new}.

@findex gtk-calendar-select-month
@findex calendar-select-month, gtk-calendar-select-month
@findex gtk-calendar-select-day
@findex calendar-select-day, gtk-calendar-select-day

The month and year currently displayed can be altered with the function
@xurl{gtk-calendar-select-month}.  The exact day can be selected from the
displayed month using the function @xurl{gtk-calendar-select-day}.

@findex gtk-calendar-mark-day
@findex calendar-mark-day, gtk-calendar-mark-day
@findex gtk-calendar-unmark-day
@findex calendar-unmark-day, gtk-calendar-unmark-day
@findex gtk-calendar-clear-marks
@findex calendar-clear-marks, gtk-calendar-clear-marks

To place a visual marker on a particular day, use the function
@xurl{gtk-calendar-mark-day} and to remove the marker the function
@xurl{gtk-calendar-unmark-day}.  Alternative, all marks can be cleared with
the function @xurl{gtk-calendar-clear-marks}.

@findex gtk-calendar-set-display-options
@findex calendar-set-display-options, gtk-calendar-set-display-options

The way in which the calendar itself is displayed can be altered using the
function @xurl{gtk-calendar-set-display-options}.

@findex gtk-calendar-get-date
@findex calendar-get-date, gtk-calendar-get-date

The selected date can be retrieved from a @xurl{gtk-calendar} using the function
@xurl{gtk-calendar-get-date}.

Users should be aware that, although the Gregorian calendar is the legal
calendar in most countries, it was adopted progressively between 1582 and
1929.  Display before these dates is likely to be historically incorrect.

@findex gtk-calendar-set-detail-func
@findex calendar-set-detail-func, gtk-calendar-set-detail-func

@ref{example-calendar} shows a brief example of the calendar widget.  It is
possible to set a special function with the function
@xurl{gtk-calendar-set-detail-func}.  The example uses this to show a tooltip
whenever the 12th day of a month is selected.

@float Example, example-calendar
@caption{Example Calendar}
@end float
@verbatim
(defun example-calendar ()
  (within-main-loop
    (let ((window (make-instance 'gtk-window
                                 :title "Example Calendar"
                                 :type :toplevel
                                 :border-width 24
                                 :default-width 250
                                 :default-height 100))
          (frame (make-instance 'gtk-frame))
          (calendar (make-instance 'gtk-calendar
                                   :show-details nil)))
      ;; Connect a signal handler to the window
      (g-signal-connect window "destroy"
                        (lambda (widget)
                          (declare (ignore widget))
                          (leave-gtk-main)))
      ;; Connect a signal handler to print the selected day
      (g-signal-connect calendar "day-selected"
                        (lambda (widget)
                          (declare (ignore widget))
                          (format t "selected: year ~A month ~A day ~A~%"
                                  (gtk-calendar-year calendar)
                                  (gtk-calendar-month calendar)
                                  (gtk-calendar-day calendar))))
      ;; Install a calendar detail function
      (gtk-calendar-set-detail-func calendar
                                    (lambda (calendar year month day)
                                      (declare (ignore calendar year month))
                                      (when (= day 12)
                                        "This day has a tooltip.")))
      ;; Mark a day
      (gtk-calendar-mark-day calendar 6)
      ;; Put the calendar into the frame and the frame into the window.
      (gtk-container-add frame calendar)
      (gtk-container-add window frame)
      (gtk-widget-show-all window))))
@end verbatim

@c -----------------------------------------------------------------------------
@node Event Box, Text Entries, Calendar, Top
@section Event Box

@findex GtkEventBox
@findex gtk-event-box
@findex event-box, gtk-event-box

@ifnotinfo
@float Figure, figure-event-box
@caption{Event Box}
@center @image{figures/event-box, 189pt}
@end float
@end ifnotinfo

Some GTK+ widgets do not have associated X windows, so these widgets just draw
on their parents.  Because of this, they cannot receive events and if they are
incorrectly sized, they do not clip so you can get messy overwriting.  If you
require more from these widgets, the @code{GtkEventBox} widget is for you.

At first glance, the @code{GtkEventBox} widget might appear to be totally
useless.  It draws nothing on the screen and responds to no events.  However,
it does serve a function - it provides an X window for its child widget.  This
is important as many GTK+ widgets do not have an associated X window.  Not
having an X window saves memory and improves performance, but also has some
drawbacks.  A widget without an X window cannot receive events, and does not
perform any clipping on its contents.  Although the name @code{GtkEventBox}
emphasizes the event-handling function, the widget can also be used for
clipping.

@findex gtk-event-box-new
@findex event-box-new, gtk-event-box-new
@findex gtk-widget-events
@findex widget-events, gtk-widget-events
@findex gtk-widget-realize
@findex widget-realize, gtk-widget-realize

To create a new @code{GtkEventBox} widget, use the call
@code{(make-instance 'gtk-event-box)} or the function @code{gtk-event-box-new}.
A child widget can then be added to this @code{GtkEventBox} with the function
@xurl{gtk-container-add}.  With the function @code{gtk-widget-events} the
events are set for the event box which can be connected to a signal handler.
To create the resources associated with an event box, the function
@code{gtk-widget-realize} has to be called explicitly for the @code{GtkEventBox}
widget.

@ref{example-event-box} demonstrates both uses of a @code{GtkEventBox} widget -
a label is created that is clipped to a small box, and set up so that a
mouse-click on the label causes the program to exit.  Resizing the window
reveals varying amounts of the label.

@findex GdkWindow
@findex gdk-window
@findex window, gdk-window

@findex GdkCursor
@findex gdk-cursor
@findex cursor, gdk-cursor

@findex GdkCursorType
@findex gdk-cursor-type
@findex cursor-type, gdk-cursor-type

@findex gdk-cursor-new
@findex cursor-new, gdk-cursor-new

@findex gtk-widget-window
@findex widget-window, gtk-widget-window
@findex gdk-window-set-cursor
@findex window-set-cursor, gdk-window-set-cursor

@findex :hand1, GdkCursorType

In addition, @ref{example-event-box} shows how to change the mouse pointer
over a window.  Every widget has an associated window of type @code{GdkWindow},
which can be get with the function @code{gtk-widget-window}. The function
@code{gdk-window-set-cursor} sets a mouse pointer for this @code{GdkWindow}.
A new mouse pointer is created with the function @code{gdk-cursor-new}.  The
functions takes one argument, which is a keyword of the enumeration type
@code{GdkCursorType} for a predefined mouse pointer.  GTK+ has about 150
predefined mouse pointers.  Look at the implementation of @code{GdkCursorType}
in the file @code{gdk.cursor.lisp} to see all possible keywords.  In
@ref{example-event-box} the mouse pointer @code{:hand1} is chosen.  This
mouse pointer is than associated to the @code{GdkWindow} with the function
@code{gdk-window-set-cursor}.

@float Example, example-event-box
@caption{Event Box}
@end float
@verbatim
(defun example-event-box ()
  (within-main-loop
    (let ((window (make-instance 'gtk-window
                                 :type :toplevel
                                 :title "Example Event Box"
                                 :border-width 12))
          (eventbox (make-instance 'gtk-event-box))
          (label (make-instance 'gtk-label
                                :label
                                "Click here to quit, and more text, more")))
      (g-signal-connect window "destroy"
                        (lambda (widget)
                          (declare (ignore widget))
                          (leave-gtk-main)))
      ;; Set the events for the event box
      (setf (gtk-widget-events eventbox) :button-press-mask)
      ;; Connect a signal to the eventbox
      (g-signal-connect eventbox "button-press-event"
                        (lambda (widget event)
                          (declare (ignore widget event))
                          (gtk-widget-destroy window)))
      ;; Add the label to the event box and the event box to the window
      (gtk-container-add eventbox label)
      (gtk-container-add window eventbox)
      ;; Realize the event box
      (gtk-widget-realize eventbox)
      ;; Set a new cursor for the event box
      (gdk-window-set-cursor (gtk-widget-window eventbox)
                             (gdk-cursor-new :hand1))
      ;; Show the window
      (gtk-widget-show-all window))))
@end verbatim


@c -----------------------------------------------------------------------------
@node Text Entries, Spin Buttons, Event Box, Top
@section Text Entries

@ifnotinfo
@float Figure, figure-text-entry
@caption{Text Entry}
@center @image{figures/text-entry256x106, 171pt}
@end float
@end ifnotinfo

The entry widget allows text to be typed and displayed in a single line text
box.  The text may be set with function calls that allow new text to replace,
prepend or append the current contents of the Entry widget.

Create a new Entry widget with the function @code{gtk-entry-new}.  The generic
function @code{gtk-entry-text} alters the text which is currently within the
entry widget.  The function @code{gtk-entry-text} sets the contents of the entry
widget, replacing the current contents.  Note that the class entry implements
the editable interface which contains some more functions for manipulating the
contents.

The contents of the entry can be retrieved by using a call to the function
@code{gtk-entry-text}.  This is useful in the callback functions described
below.

If we do not want the contents of the entry to be changed by someone typing
into it, we can change its editable state with the function
@code{gtk-editable-set-editable}.  This function allows us to toggle the
editable state of the entry widget by passing in a @code{T} or @code{NIL} value
for the editable argument.

If we are using the entry where we do not want the text entered to be visible,
for example when a password is being entered, we can use the function
@code{gtk-entry-visibility}, which also takes a boolean flag.

A region of the text may be set as selected by using the function
@code{gtk-editable-select-region}.  This would most often be used after setting
some default text in an Entry, making it easy for the user to remove it.

If we want to catch when the user has entered text, we can connect to the
activate or changed signal.  Activate is raised when the user hits the enter key
within the entry widget.  Changed is raised when the text changes at all, e.g.,
for every character entered or removed.

@ref{example-text-entry} is an example of using an entry widget.

@float Example, example-text-entry
@caption{Text Entry}
@end float
@verbatim
(defun example-text-entry ()
  (within-main-loop
    (let* ((window (make-instance 'gtk-window
                                  :type :toplevel
                                  :title "Example Text Entry"
                                  :default-width 250))
           (vbox (make-instance 'gtk-vbox))
           (hbox (make-instance 'gtk-hbox))
           (entry (make-instance 'gtk-entry
                                 :text "Hello"
                                 :max-length 50))
           (pos (gtk-entry-text-length entry)))
      (g-signal-connect window "destroy"
                        (lambda (widget)
                          (declare (ignore widget))
                          (leave-gtk-main)))
      (g-signal-connect entry "activate"
                        (lambda (widget)
                          (declare (ignore widget))
                          (format t "Entry contents: ~A"
                                  (gtk-entry-text entry))))
      (gtk-editable-insert-text entry " world" pos)
      (gtk-editable-select-region entry 0 (gtk-entry-text-length entry))
      (gtk-box-pack-start vbox entry :expand t :fill t :padding 0)
      (let ((check (gtk-check-button-new-with-label "Editable")))
        (g-signal-connect check "toggled"
           (lambda (widget)
             (declare (ignore widget))
             (gtk-editable-set-editable
                                      entry
                                      (gtk-toggle-button-active check))))
        (gtk-box-pack-start hbox check))
      (let ((check (gtk-check-button-new-with-label "Visible")))
        (setf (gtk-toggle-button-active check) t)
        (g-signal-connect check "toggled"
           (lambda (widget)
             (declare (ignore widget))
             (setf (gtk-entry-visibility entry)
                   (gtk-toggle-button-active check))))
        (gtk-box-pack-start hbox check))
      (gtk-box-pack-start vbox hbox)
      (let ((button (gtk-button-new-from-stock "gtk-close")))
        (g-signal-connect button "clicked"
                          (lambda (widget)
                            (declare (ignore widget))
                            (gtk-widget-destroy window)))
        (gtk-box-pack-start vbox button))
      (gtk-container-add window vbox)
      (gtk-widget-show window))))
@end verbatim


@c -----------------------------------------------------------------------------
@node Spin Buttons, Combo Box, Text Entries, Top
@section Spin Buttons

@ifnotinfo
@float Figure, figure-spin-button
@caption{Spin Button}
@center @image{figures/spin-button306x347, 204pt}
@end float
@end ifnotinfo

The spin button widget is generally used to allow the user to select a value
from a range of numeric values.  It consists of a text entry box with up and
down arrow buttons attached to the side.  Selecting one of the buttons causes
the value to "spin" up and down the range of possible values.  The entry box
may also be edited directly to enter a specific value.

The spin button allows the value to have zero or a number of decimal places and
to be incremented or decremented in configurable steps.  The action of holding
down one of the buttons optionally results in an acceleration of change in the
value according to how long it is depressed.

The spin button uses an adjustment object to hold information about the range
of values that the spin button can take. This makes for a powerful spin button
widget.

Recall that an adjustment object is created with the function
@code{gtk-adjustment-new}, which has the arguments @code{value}, @code{lower},
@code{step-increment}, @code{page-increment}, and @code{page-size}.  These
properties of an adjustment are used by the spin button in the following
way:

@table @code
@item value
      initial value for the Spin Button
@item lower
      lower range value
@item upper
      upper range value
@item step-increment
      value to increment/decrement when pressing mouse button 1 on a button
@item page_increment
      value to increment/decrement when pressing mouse button 2 on a button
@item page_size
      unused
@end table

Additionally, mouse button 3 can be used to jump directly to the upper or lower
values when used to select one of the buttons.  A spin button is created with
the function @code{gtk-spin-button-new}, which as the arguments
@code{adjustment}, @code{climb-rate}, and @code{digits}.

The @code{climb-rate} argument take a value between 0.0 and 1.0 and indicates
the amount of acceleration that the spin button has.  The @code{digits} argument
specifies the number of decimal places to which the value will be displayed.

A spin button can be reconfigured after creation using the function
@code{gtk-spin-button-configure}.  The first argument specifies the spin button
that is to be reconfigured.  The other arguments are as specified for the
function @code{gtk-spin-button-new}.

The adjustment can be set and retrieved independently using the two
functions @code{gtk-spin-button-set-adjustment} and
@code{gtk-spin-button-get-adjustment}.

The number of decimal places can also be altered using the function
@code{gtk-spin-button-set-digits} and the value that a spin button is currently
displaying can be changed using the function @code{gtk-spin-button-set-value}.

The current value of a spin button can be retrieved as either a floating point
or integer value with the functions @code{gtk-spin-button-value} and
@code{gtk-spin-button-get-value-as-int}.

If you want to alter the value of a spin button relative to its current value,
then the function @code{gtk-spin-button-spin} can be used, which has the three
arguments @code{spin-button}, @code{direction}, and @code{increment}.  The
argument @code{direction} is of the enumeration type @code{GtkSpinType}, which
can take one of the values shown in @ref{table-gtk-spin-type}.

@float Table, table-gtk-spin-type
@caption{The values of the @code{GtkSpinType} enumeration are used to specify
the change to make in @code{gtk-spin-button-spin}}
@shortcaption{Values of the type @code{GtkSpinType}}
@end float
@table @code
@item :step-forward
      Increment by the adjustments step increment.
@item :backward
      Decrement by the adjustments step increment.
@item :forward
      Increment by the adjustments page increment.
@item :page-backward
      Decrement by the adjustments page increment.
@item :home
      Go to the adjustments lower bound.
@item :end
      Go to the adjustments upper bound.
@item :user-defined
      Change by a specified amount.
@end table

@code{:step-forward} and @code{:step-backward} change the value of the spin
button by the amount specified by increment, unless increment is equal
to 0, in which case the value is changed by the value of @code{step-increment}
in the adjustment.

@code{:page-forward} and @code{:page-backward} simply alter the value of the
spin button by @code{increment}.

@code{:home} sets the value of the spin button to the bottom of the adjustments
range and @code{:end} sets the value of the spin button to the top of the
adjustments range.

@code{:user-defined} simply alters the value of the spin button by the
specified amount.

We move away from functions for setting and retrieving the range attributes of
the spin button now, and move onto functions that affect the appearance and
behavior of the spin button widget itself.

The first of these functions is @code{gtk-spin-button-set-numeric}, which is
used to constrain the text box of the spin button such that it may only contain
a numeric value.  This prevents a user from typing anything other than numeric
values into the text box of a spin button.

You can set whether a Spin Button will wrap around between the upper and lower
range values with the function @code{gtk-spin-button-set-wrap}.  You can set a
spin button to round the value to the nearest @code{step-increment}, which is
set within the adjustment object used with the spin button.  This is
accomplished with the function @code{gtk-spin-button-set-snap-to-ticks}.

The update policy of a spin button can be changed with the function
@code{gtk-spin-button-set-update-policy}.  The possible values of policy are
either @code{:always} or @code{:if-valid}.  These policies affect the behavior
of a Spin Button when parsing inserted text and syncing its value with the
values of the adjustment.

In the case of @code{:if-valid} the spin button value only gets changed if the
text input is a numeric value that is within the range specified by the
adjustment.  Otherwise the text is reset to the current value.  In case of
@code{:always} we ignore errors while converting text into a numeric value.

Finally, you can explicitly request that a spin button update itself with the
function @code{gtk-spin-button-update}.

@float Example, example-spin-button
@caption{Spin Button}
@end float
@verbatim
(defun example-spin-button ()
  (within-main-loop
    (let ((window (make-instance 'gtk-window
                                 :type :toplevel
                                 :title "Example Spin Button"
                                 :default-width 300))
          (vbox (make-instance 'gtk-vbox
                               :homogeneous nil
                               :spacing 6
                               :border-width 12))
          (vbox1 (make-instance 'gtk-vbox
                                :homogeneous nil
                                :spacing 0
                                :border-width 6))
          (vbox2 (make-instance 'gtk-vbox
                                :homogeneous nil
                                :spacing 0
                                :boder-width 6))
          (hbox (make-instance 'gtk-hbox))
          (frame1 (make-instance 'gtk-frame
                                 :label "Not accelerated"))
          (frame2 (make-instance 'gtk-frame
                                 :label "Accelerated"))
          (label (make-instance 'gtk-label
                                :label "0")))
      (g-signal-connect window "destroy"
                        (lambda (widget)
                          (declare (ignore widget))
                          (leave-gtk-main)))
      (let ((vbox (make-instance 'gtk-vbox))
            (spinner (make-instance 'gtk-spin-button
                                    :adjustment
                                    (make-instance 'gtk-adjustment
                                                   :value 1.0
                                                   :lower 1.0
                                                   :upper 31.0
                                                   :step-increment 1.0
                                                   :page-increment 5.0
                                                   :page-size 0.0)
                                    :climb-rate 0
                                    :digits 0
                                    :wrap t)))
        (gtk-box-pack-start vbox
                            (make-instance 'gtk-label
                                           :label "Day :"
                                           :xalign 0
                                           :yalign 0.5)
                            :expand nil)
        (gtk-box-pack-start vbox spinner :expand nil)
        (gtk-box-pack-start hbox vbox :padding 6))
      (let ((vbox (make-instance 'gtk-vbox))
            (spinner (make-instance 'gtk-spin-button
                                    :adjustment
                                    (make-instance 'gtk-adjustment
                                                   :value 1.0
                                                   :lower 1.0
                                                   :upper 12.0
                                                   :step-increment 1.0
                                                   :page-increment 5.0
                                                   :page-size 0.0)
                                    :climb-rate 0
                                    :digits 0
                                    :wrap t)))
        (gtk-box-pack-start vbox
                            (make-instance 'gtk-label
                                           :label "Month :"
                                           :xalign 0
                                           :yalign 0.5)
                            :expand nil)
        (gtk-box-pack-start vbox spinner :expand nil)
        (gtk-box-pack-start hbox vbox :padding 6))
      (let ((vbox (make-instance 'gtk-vbox))
            (spinner (make-instance 'gtk-spin-button
                                    :adjustment
                                    (make-instance 'gtk-adjustment
                                                   :value 1.0
                                                   :lower 1998.0
                                                   :upper 2100.0
                                                   :step-increment 1.0
                                                   :page-increment 100.0
                                                   :page-size 0.0)
                                    :climb-rate 0
                                    :digits 0
                                    :wrap t)))
        (gtk-box-pack-start vbox
                            (make-instance 'gtk-label
                                           :label "Year :"
                                           :xalign 0
                                           :yalign 0.5)
                            :expand nil)
        (gtk-box-pack-start vbox spinner :expand nil :fill t)
        (gtk-box-pack-start hbox vbox :padding 6))
      (gtk-box-pack-start vbox1 hbox :padding 6)
      (gtk-container-add frame1 vbox1)
      (gtk-box-pack-start vbox frame1)
      (setq hbox (make-instance 'gtk-hbox))
      (let ((vbox (make-instance 'gtk-vbox))
            (spinner1 (make-instance 'gtk-spin-button
                                     :adjustment
                                     (make-instance 'gtk-adjustment
                                                    :value 1.0
                                                    :lower -10000.0
                                                    :upper  10000.0
                                                    :step-increment 0.5
                                                    :page-increment 100.0
                                                    :page-size 0.0)
                                     :climb-rate 1.0
                                     :digits 2
                                     :wrap t))
            (spinner2 (make-instance 'gtk-spin-button
                                     :adjustment
                                     (make-instance 'gtk-adjustment
                                                    :value 2
                                                    :lower 1
                                                    :upper 5
                                                    :step-increment 1
                                                    :page-increment 1
                                                    :page-size 0)
                                     :climb-rate 0.0
                                     :digits 0
                                     :wrap t)))
        (gtk-box-pack-start vbox
                            (make-instance 'gtk-label
                                           :label "Value :"
                                           :xalign 0
                                           :yalign 0.5)
                            :fill t)
        (gtk-box-pack-start vbox spinner1 :expand nil)
        (gtk-box-pack-start hbox vbox :padding 6)
        (g-signal-connect spinner2 "value-changed"
           (lambda (widget)
             (declare (ignore widget))
             (gtk-spin-button-set-digits
                               spinner1
                               (gtk-spin-button-get-value-as-int spinner2))))
        (setq vbox (make-instance 'gtk-vbox))
        (gtk-box-pack-start vbox
                            (make-instance 'gtk-label
                                           :label "Digits :"
                                           :xalign 0
                                           :yalign 0.5)
                            :expand nil)
        (gtk-box-pack-start vbox spinner2 :expand nil)
        (gtk-box-pack-start hbox vbox :padding 6)
        (gtk-box-pack-start vbox2 hbox :padding 6)
        (let ((check (make-instance 'gtk-check-button
                                    :label "Snap to 0.5-ticks"
                                    :active t)))
          (g-signal-connect check "clicked"
             (lambda (widget)
               (gtk-spin-button-set-snap-to-ticks
                                     spinner1
                                     (gtk-toggle-button-active widget))))
          (gtk-box-pack-start vbox2 check))
        (let ((check (make-instance 'gtk-check-button
                                    :label "Numeric only input mode"
                                    :active t)))
          (g-signal-connect check "clicked"
             (lambda (widget)
               (gtk-spin-button-set-numeric
                                     spinner1
                                     (gtk-toggle-button-active widget))))
          (gtk-box-pack-start vbox2 check))
        (gtk-container-add frame2 vbox2)
        (setq hbox (make-instance 'gtk-hbox))
        (let ((button (gtk-button-new-with-label "Value as Int")))
          (g-signal-connect button "clicked"
             (lambda (widget)
               (declare (ignore widget))
               (gtk-label-set-text
                      label
                      (format nil "~A"
                              (gtk-spin-button-get-value-as-int spinner1)))))
            (gtk-box-pack-start hbox button))
        (let ((button (gtk-button-new-with-label "Value as Float")))
          (g-signal-connect button "clicked"
             (lambda (widget)
               (declare (ignore widget))
               (gtk-label-set-text
                             label
                             (format nil "~A"
                                     (gtk-spin-button-value spinner1)))))
          (gtk-box-pack-start hbox button))
        (gtk-box-pack-start vbox2 hbox)
        (gtk-box-pack-start vbox2 label))
      (gtk-box-pack-start vbox frame2)
      (let ((button (make-instance 'gtk-button
                                   :label "Close")))
        (g-signal-connect button "clicked"
                          (lambda (widget)
                            (declare (ignore widget))
                            (gtk-widget-destroy window)))
        (gtk-box-pack-start vbox button))
      (gtk-container-add window vbox)
      (gtk-widget-show window))))
@end verbatim


@c -----------------------------------------------------------------------------
@need 800
@node Combo Box, Calendar, Spin Buttons, Top
@section Combo Box

@menu
* General Combo Box::
* Combo Box Text::
@end menu

@c -----------------------------------------------------------------------------
@node General Combo Box, Combo Box Text, Top, Top
@subsection General Combo Box

@findex GtkComboBox
@findex gtk-combo-box
@findex combo-box, gtk-combo-box

@ifnotinfo
@float Figure, figure-combo-box
@caption{Combo Box}
@center @image{figures/combo-box386x131, 257pt}
@end float
@end ifnotinfo

A @xurl{gtk-combo-box} is a widget that allows the user to choose from a list of
valid choices.  The @xurl{gtk-combo-box} displays the selected choice.  When
activated, the @xurl{gtk-combo-box} displays a popup which allows the user to
make a new choice.  The style in which the selected value is displayed, and the
style of the popup is determined by the current theme.  It may be similar to a
Windows-style combo box.

@findex GtkCellLayout
@findex gtk-cell-layout
@findex cell-layout, gtk-cell-layout

The @xurl{gtk-combo-box} uses the model-view pattern; the list of valid choices
is specified in the form of a tree model, and the display of the choices can be
adapted to the data in the model by using cell renderers, as you would in a
tree view.  This is possible since @xurl{gtk-combo-box} implements the
@xurl{gtk-cell-layout} interface.  The tree model holding the valid choices is
not restricted to a flat list, it can be a real tree, and the popup will reflect
the tree structure.

@findex has-entry
@findex GtkEntry
@findex gtk-entry
@findex entry, gtk-entry

@findex gtk-bin-get-child
@findex bin-get-child, gtk-bin-get-child

To allow the user to enter values not in the model, the @code{has-entry}
property allows the @xurl{gtk-combo-box} to contain a @xurl{gtk-entry}.  This
entry can be accessed by calling @xurl{gtk-bin-get-child} on the combo box.

For a simple list of textual choices, the model-view API of @xurl{gtk-combo-box}
can be a bit overwhelming.  In this case, @xurl{gtk-combo-box-text} offers a
simple alternative.  Both @xurl{gtk-combo-box} and @xurl{gtk-combo-box-text} can
contain an entry.

@float Example, example-combo-box
@caption{Combo Box}
@end float
@verbatim
(defun example-combo-box ()
  (within-main-loop
    (let* ((window (make-instance 'gtk-window
                                  :type :toplevel
                                  :border-width 12
                                  :title "Example Combo Box"))
           (model (make-instance 'gtk-list-store
                                 :column-types '("gchararray" "gint")))
           (combo-box (make-instance 'gtk-combo-box :model model))
           (title-label (make-instance 'gtk-label :label "Title:"))
           (value-label (make-instance 'gtk-label :label "Value:"))
           (title-entry (make-instance 'gtk-entry))
           (value-entry (make-instance 'gtk-entry))
           (button (make-instance 'gtk-button :label "Add"))
           (table (make-instance 'gtk-table
                                 :n-rows 3
                                 :n-columns 3)))
      ;; Fill in data into the columns
      (gtk-list-store-set model (gtk-list-store-append model) "Monday" 1)
      (gtk-list-store-set model (gtk-list-store-append model) "Tuesday" 2)
      (gtk-list-store-set model (gtk-list-store-append model) "Wednesday" 3)
      (gtk-list-store-set model (gtk-list-store-append model) "Thursday" 4)
      (gtk-list-store-set model (gtk-list-store-append model) "Friday" 5)
      (gtk-list-store-set model (gtk-list-store-append model) "Saturday" 6)
      (gtk-list-store-set model (gtk-list-store-append model) "Sunday" 7)
      ;; Set the first entry to active
      (gtk-combo-box-set-active combo-box 0)
      ;; Define the signal handlers
      (g-signal-connect window "destroy"
                        (lambda (w)
                          (declare (ignore w))
                          (leave-gtk-main)))
      (g-signal-connect button "clicked"
         (lambda (widget)
           (declare (ignore widget))
           (gtk-list-store-set model
                               (gtk-list-store-append model)
                               (gtk-entry-text title-entry)
                               (or (parse-integer
                                     (gtk-entry-text value-entry)
                                     :junk-allowed t)
                                   0))))
      (g-signal-connect combo-box "changed"
         (lambda (widget)
           (declare (ignore widget))
           (let ((dialog (gtk-message-dialog-new
                             window
                             '(:destroy-with-parent)
                             :info
                             :close
                             "You selected row ~A"
                             (gtk-combo-box-get-active combo-box))))
             (gtk-dialog-run dialog)
             (gtk-widget-destroy dialog))))
      ;; Create renderers for the cells
      (let ((renderer (make-instance 'gtk-cell-renderer-text
                                     :text "A text")))
        (gtk-cell-layout-pack-start combo-box renderer :expand t)
        (gtk-cell-layout-add-attribute combo-box renderer "text" 0))
      (let ((renderer (make-instance 'gtk-cell-renderer-text
                                     :text "A number")))
        (gtk-cell-layout-pack-start combo-box renderer :expand nil)
        (gtk-cell-layout-add-attribute combo-box renderer "text" 1))
      ;; Align the labels
      (gtk-misc-set-alignment title-label 0.0 0.0)
      (gtk-misc-set-alignment value-label 0.0 0.0)
      ;; Put the widgets into the table
      (gtk-table-attach table title-label 0 1 0 1)
      (gtk-table-attach table value-label 1 2 0 1)
      (gtk-table-attach table title-entry 0 1 1 2)
      (gtk-table-attach table value-entry 1 2 1 2)
      (gtk-table-attach table button      2 3 1 2)
      (gtk-table-attach table combo-box   0 3 2 3)
      ;; Put the table into the window
      (gtk-container-add window table)
      ;; Show the window
      (gtk-widget-show-all window))))
@end verbatim

@c -----------------------------------------------------------------------------
@node Combo Box Text, , General Combo Box, Top
@subsection Combo Box Text

@findex GtkComboBoxText
@findex gtk-combo-box-text
@findex combo-box-text, gtk-combo-box-text

@ifnotinfo
@float Figure, figure-combo-box-text
@caption{Combo Box Text}
@center @image{figures/combo-box-text151x85, 101pt}
@end float
@end ifnotinfo

A @xurl{gtk-combo-box-text} is a simple variant of @xurl{gtk-combo-box} that
hides the model-view complexity for simple text-only use cases.

@findex gtk-combo-box-text-new
@findex gtk-combo-box-text-new-with-entry

@findex combo-box-text-new, gtk-combo-box-text-new
@findex combo-box-text-new-with-entry, gtk-combo-box-text-new-with-entry

To create a @xurl{gtk-combo-box-text}, use the functions
@xurl{gtk-combo-box-text-new} or @xurl{gtk-combo-box-text-new-with-entry}.

@findex gtk-combo-box-text-append-text
@findex gtk-combo-box-text-insert-text
@findex gtk-combo-box-text-prepend-text
@findex gtk-combo-box-text-remove

@findex combo-box-text-append-text, gtk-combo-box-text-append-text
@findex combo-box-text-insert-text, gtk-combo-box-text-insert-text
@findex combo-box-text-prepend-text, gtk-combo-box-text-prepend-text
@findex combo-box-text-remove, gtk-combo-box-text-remove

You can add items to a @xurl{gtk-combo-box-text} with the functions
@xurl{gtk-combo-box-text-append-text}, @xurl{gtk-combo-box-text-insert-text} or
@xurl{gtk-combo-box-text-prepend-text} and remove options with the function
@xurl{gtk-combo-box-text-remove}.

@findex gtk-combo-box-text-get-active-text
@findex gtk-bin-get-child

@findex combo-box-text-get-active-tect, gtk-combo-box-text-get-active-text
@findex bin-get-child, gtk-bin-get-child

If the @xurl{gtk-combo-box-text} contains an entry via the @code{has-entry}
property, its contents can be retrieved using
@xurl{gtk-combo-box-text-get-active-text}.  The entry itself can be accessed by
calling @xurl{gtk-bin-get-child} on the combo box.

@float Example, example-combo-box-text
@caption{Combo Box Text}
@end float
@verbatim
(defun example-combo-box-text ()
  (within-main-loop
    (let ((window (make-instance 'gtk-window
                                 :border-width 12
                                 :title "Example Combo Box Text"))
          (combo (make-instance 'gtk-combo-box-text)))
      (gtk-combo-box-text-append-text combo "First entry")
      (gtk-combo-box-text-append-text combo "Second entry")
      (gtk-combo-box-text-append-text combo "Third entry")
      (gtk-combo-box-set-active combo 0)
      (gtk-container-add window combo)
      (gtk-widget-show window))))
@end verbatim


