{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts #-}

-- | HTk\'s /ComboBox/.
-- Only available when using tixwish.  However this module needs to go
-- in the uni-htk-widgets package because it depends on it.
module HTk.Widgets.ComboBox (

  ComboBox,
  newComboBox,

  pick,
  entrySubwidget,
  listBoxSubwidget

) where

import HTk.Kernel.Core
import HTk.Kernel.BaseClasses(Widget)
import HTk.Kernel.Configuration
import Util.Computation
import Events.Synchronized
import Events.Destructible
import HTk.Kernel.Packer
import HTk.Widgets.Entry
import HTk.Widgets.ListBox
import HTk.Tix.Subwidget


-- -----------------------------------------------------------------------
-- datatype
-- -----------------------------------------------------------------------

-- | The @ComboBox@ datatype.
-- A ComboBox is a so called mega widget composed of an entry widget
-- and a list box. Both subwidgets are accessible by themselves.

data GUIValue a =>
          ComboBox a = ComboBox GUIOBJECT (Entry a) (ListBox a)
                                   deriving Eq

-- | Retrieve the entry subwidget of a combo box.
entrySubwidget :: GUIValue a => ComboBox a -> Entry a
entrySubwidget (ComboBox _ x _) = x

-- | Retrieve the list box subwidget of a combo box.
listBoxSubwidget :: GUIValue a => ComboBox a -> ListBox a
listBoxSubwidget (ComboBox _ _ x) = x

-- -----------------------------------------------------------------------
-- combo box creation
-- -----------------------------------------------------------------------

-- | Constructs a new combo box and returns a handler.
newComboBox :: (GUIValue a, Container par) =>
   par
   -- ^ the list of configuration options for this
   -- combo box.
   -> Bool
   -- ^ true if the user should be allowed to type into the
   -- entry of the ComboBox.
   -> [Config (ComboBox a)]
   -> IO (ComboBox a)
   -- ^ A combo box.
newComboBox par editable cnf =
  do
    cb <- createGUIObject (toGUIObject par) (COMBOBOX editable) comboBoxMethods
    e  <- createAsSubwidget cb
    lb <- createAsSubwidget cb
    configure (ComboBox cb e lb) cnf


-- -----------------------------------------------------------------------
-- combo box specific commands and options
-- -----------------------------------------------------------------------

-- | Sets the index item in the listbox to be the current value of the
-- ComboBox.
pick :: GUIValue a => Int -> Config (ComboBox a)
pick i cb = execMethod cb (\nm -> tkPick nm i) >> return cb

-- -----------------------------------------------------------------------
-- combo box methods
-- -----------------------------------------------------------------------

comboBoxMethods :: Methods
comboBoxMethods = Methods (cgetCmd defMethods)
                          (csetCmd defMethods)
                          tkCreateComboBox
                          (packCmd defMethods)
                          (gridCmd defMethods)
                          (destroyCmd defMethods)
                          (bindCmd defMethods)
                          (unbindCmd defMethods)
                          (cleanupCmd defMethods)


-- -----------------------------------------------------------------------
-- combo box instances
-- -----------------------------------------------------------------------

-- | Internal.
instance GUIValue a => GUIObject (ComboBox a) where
  toGUIObject (ComboBox f _ _) = f

  cname _ = "ComboBox"

-- | The value of a combo box is the list of the displayed objects (these
-- are instances of class @GUIValue@ and therefore instances
-- of class @Show@).
instance (GUIValue a, GUIValue [a]) => HasValue (ComboBox a) [a] where
  value vals w =
    execMethod w (\nm -> tkInsert nm 0 (map toGUIValue vals)) >> return w
  -- Gets the list of displayed objects.
  getValue w = evalMethod w (\nm -> tkGet nm)

-- | A combo box has standard widget properties (focus, cursor, ...).
instance GUIValue a => Widget (ComboBox a)

-- | A combo box widget can be destroyed.
instance GUIValue a => Destroyable (ComboBox a) where
  -- Destroys a combo box widget.
  destroy = destroy . toGUIObject

-- | A combo box widget has a configureable border.
instance GUIValue a => HasBorder (ComboBox a)

-- | A combo box widget has a text anchor.
instance GUIValue a => HasAnchor (ComboBox a)

-- | A combo box widget has a background colour.
instance GUIValue a => HasColour (ComboBox a) where
  legalColourID = hasBackGroundColour

-- | You can specify the size of a combo box widget-
instance GUIValue a => HasSize (ComboBox a)

-- | You can synchronize on a combo box widget.
instance GUIValue a => Synchronized (ComboBox a) where
  -- Synchronizes on a combo box widget.
  synchronize = synchronize . toGUIObject

-- | A combo box widget is a stateful widget, it can be enabled or disabled.
instance GUIValue a => HasEnable (ComboBox a)

-- -----------------------------------------------------------------------
-- Tk commands
-- -----------------------------------------------------------------------

tkCreateComboBox :: ObjectName -> ObjectKind -> ObjectName ->
                    ObjectID -> [ConfigOption] -> TclScript
tkCreateComboBox _ (COMBOBOX editable) name _ opts =
  ["tixComboBox " ++ show name ++ " -editable " ++ show editable ++
   showConfigs opts]
tkCreateComboBox _ _ _ _ _ = []
{-# INLINE tkCreateComboBox #-}

tkInsert ::  ObjectName -> Int -> [GUIVALUE] -> TclScript
tkInsert name inx elems =
  [tkDelete name "0" "end",
   show name ++ " subwidget listbox insert " ++ show inx ++ " " ++
   showElements elems]
{-# INLINE tkInsert #-}

tkDelete :: ObjectName -> String -> String -> TclCmd
tkDelete name first last =
  show name ++ " subwidget listbox delete " ++ first ++ " " ++ last
{-# INLINE tkDelete #-}

tkGet :: ObjectName -> TclScript
tkGet name = [show name ++ " subwidget entry get"]
{-# INLINE tkGet #-}

showElements :: [GUIVALUE] -> String
showElements = concatMap (++ " ") . (map show)
{-# INLINE showElements #-}

tkPick :: ObjectName -> Int -> TclScript
tkPick name index = [show name ++ " pick " ++ show index]
{-# INLINE tkPick #-}