{-# LANGUAGE CPP #-}
-- -*-haskell-*-
--  GIMP Toolkit (GTK) Widget Container
--
--  Author : Axel Simon
--
--  Created: 15 May 2001
--
--  Copyright (C) 1999-2005 Axel Simon
--
--  This library is free software; you can redistribute it and/or
--  modify it under the terms of the GNU Lesser General Public
--  License as published by the Free Software Foundation; either
--  version 2.1 of the License, or (at your option) any later version.
--
--  This library is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
--  Lesser General Public License for more details.
--
-- |
-- Maintainer  : gtk2hs-users@lists.sourceforge.net
-- Stability   : provisional
-- Portability : portable (depends on GHC)
--
-- Base class for widgets which contain other widgets
--
module Graphics.UI.Gtk.Abstract.Container (
-- * Detail
--
-- | A Gtk+ user interface is constructed by nesting widgets inside widgets.
-- Container widgets are the inner nodes in the resulting tree of widgets: they
-- contain other widgets. So, for example, you might have a 'Window' containing
-- a 'Frame' containing a 'Label'. If you wanted an image instead of a textual
-- label inside the frame, you might replace the 'Label' widget with a 'Image'
-- widget.
--
-- There are two major kinds of container widgets in Gtk+. Both are
-- subclasses of the abstract 'Container' base class.
--
-- The first type of container widget has a single child widget and derives
-- from 'Bin'. These containers are decorators, which add some kind of
-- functionality to the child. For example, a 'Button' makes its child into a
-- clickable button; a 'Frame' draws a frame around its child and a 'Window'
-- places its child widget inside a top-level window.
--
-- The second type of container can have more than one child; its purpose is
-- to manage layout. This means that these containers assign sizes and
-- positions to their children. For example, a 'HBox' arranges its children in
-- a horizontal row, and a 'Table' arranges the widgets it contains in a
-- two-dimensional grid.
--
-- To fulfill its task, a layout container must negotiate the size
-- requirements with its parent and its children. This negotiation is carried
-- out in two phases, size requisition and size allocation.

-- ** Size Requisition
--
-- | The size requisition of a widget is it's desired width and height. This
-- is represented by a 'Requisition'.
--
-- How a widget determines its desired size depends on the widget. A
-- 'Label', for example, requests enough space to display all its text.
-- Container widgets generally base their size request on the requisitions of
-- their children.
--
-- The size requisition phase of the widget layout process operates
-- top-down. It starts at a top-level widget, typically a 'Window'. The
-- top-level widget asks its child for its size requisition by calling
-- 'widgetSizeRequest'. To determine its requisition, the child asks its own
-- children for their requisitions and so on. Finally, the top-level widget
-- will get a requisition back from its child.

-- ** Size Allocation
--
-- | When the top-level widget has determined how much space its child would
-- like to have, the second phase of the size negotiation, size allocation,
-- begins. Depending on its configuration (see 'windowSetResizable'), the
-- top-level widget may be able to expand in order to satisfy the size request
-- or it may have to ignore the size request and keep its fixed size. It then
-- tells its child widget how much space it gets by calling
-- 'widgetSizeAllocate'. The child widget divides the space among its children
-- and tells each child how much space it got, and so on. Under normal
-- circumstances, a 'Window' will always give its child the amount of space the
-- child requested.
--
-- A child's size allocation is represented by an 'Allocation'.
-- This contains not only a width and height, but also a
-- position (i.e. X and Y coordinates), so that containers can tell their
-- children not only how much space they have gotten, but also where they are
-- positioned inside the space available to the container.
--
-- Widgets are required to honor the size allocation they receive; a size
-- request is only a request, and widgets must be able to cope with any size.

-- ** Child attributes
--
-- | 'Container' introduces child attributes - these are object attributes
-- that are not specific to either the container or the contained widget, but
-- rather to their relation. Typical examples of child attributes are the
-- position or pack-type of a widget which is contained in a 'Box'.
--
-- The 'Container' class does not itself define any child attributes, they are
-- defined (and documented) by the various 'Container' subclasses.
--
-- Child attributes can be set or obtained in a similar way to ordinary
-- attributes. So ordinary attributes are set like so:
--
-- > set object [ attr := value ]
--
-- Whereas child attributes take the child object as a parameter:
--
-- > set container [ attr child := value ]
--
-- And similarly for getting a child attribute's value:
--
-- > value <- get container (attr child)
--

-- * Class Hierarchy
-- |
-- @
-- |  'GObject'
-- |   +----'Object'
-- |         +----'Widget'
-- |               +----Container
-- |                     +----'Bin'
-- |                     +----'Box'
-- |                     +----'CList'
-- |                     +----'Fixed'
-- |                     +----'Paned'
-- |                     +----'IconView'
-- |                     +----'Layout'
-- |                     +----'List'
-- |                     +----'MenuShell'
-- |                     +----'Notebook'
-- |                     +----'Socket'
-- |                     +----'Table'
-- |                     +----'TextView'
-- |                     +----'Toolbar'
-- |                     +----'TreeView'
-- @

-- * Types
  Container,
  ContainerClass,
  castToContainer, gTypeContainer,
  toContainer,
  ContainerForeachCB,
  ResizeMode(..),

-- * Methods
  containerAdd,
  containerRemove,
  containerForeach,
  containerForall,
  containerGetChildren,
  containerSetFocusChild,
  containerSetFocusChain,
  containerGetFocusChain,
  containerUnsetFocusChain,
  containerSetFocusVAdjustment,
  containerGetFocusVAdjustment,
  containerSetFocusHAdjustment,
  containerGetFocusHAdjustment,
  containerResizeChildren,
  containerSetBorderWidth,
  containerGetBorderWidth,
  containerGetResizeMode,
  containerSetResizeMode,

-- * Attributes
  containerResizeMode,
  containerBorderWidth,
  containerChild,
  containerFocusHAdjustment,
  containerFocusVAdjustment,

-- * Signals
  add,
  checkResize,
  remove,
  setFocusChild,

-- * Deprecated
#ifndef DISABLE_DEPRECATED
  onAdd,
  afterAdd,
  onCheckResize,
  afterCheckResize,
  onRemove,
  afterRemove,
  onSetFocusChild,
  afterSetFocusChild,
#endif
  ) where

import Control.Monad    (liftM)

import System.Glib.FFI
import System.Glib.Attributes
import System.Glib.Properties
import Graphics.UI.Gtk.Abstract.Object  (makeNewObject)
{#import Graphics.UI.Gtk.Types#}
{#import Graphics.UI.Gtk.Signals#}
import System.Glib.GList                (fromGList, withGList)
import Graphics.UI.Gtk.General.Enums    (ResizeMode(..))

{# context lib="gtk" prefix="gtk" #}

--------------------
-- Methods

-- | Adds @widget@ to the container. Typically used for simple containers such
-- as 'Window', 'Frame', or 'Button'; for more complicated layout containers
-- such as 'Box' or 'Table', this function will pick default packing parameters
-- that may not be correct. So consider functions such as 'boxPackStart' and
-- 'tableAttach' as an alternative to 'containerAdd' in those cases. A widget
-- may be added to only one container at a time; you can't place the same
-- widget inside two different containers.
--
containerAdd :: (ContainerClass self, WidgetClass widget) => self
 -> widget -- ^ @widget@ - a widget to be placed inside @container@
 -> IO ()
containerAdd self widget =
  {# call container_add #}
    (toContainer self)
    (toWidget widget)

-- | Removes @widget@ from @container@. @widget@ must be inside @container@.
--
containerRemove :: (ContainerClass self, WidgetClass widget) => self
 -> widget -- ^ @widget@ - a current child of @container@
 -> IO ()
containerRemove self widget =
  {# call container_remove #}
    (toContainer self)
    (toWidget widget)

-- | Maps @callback@ over each non-internal child of @container@. See
-- 'containerForall' for details on what constitutes an \"internal\" child.
-- Most applications should use 'containerForeach', rather than
-- 'containerForall'.
--
containerForeach :: ContainerClass self => self
 -> ContainerForeachCB
 -> IO ()
containerForeach self fun = do
  fPtr <- mkContainerForeachFunc (\wPtr _ -> do
    w <- makeNewObject mkWidget (return wPtr)
    fun w)
  {# call container_foreach #}
    (toContainer self)
    fPtr
    nullPtr
  freeHaskellFunPtr fPtr

-- | A function that is invoked for all widgets in a container.
type ContainerForeachCB = Widget -> IO ()
{#pointer Callback#}

foreign import ccall "wrapper" mkContainerForeachFunc ::
  (Ptr Widget -> Ptr () -> IO ()) -> IO Callback

-- | Maps @callback@ over each child of @container@, including children that
-- are considered \"internal\" (implementation details of the container).
-- \"Internal\" children generally weren't added by the user of the container,
-- but were added by the container implementation itself. Most applications
-- should use 'containerForeach', rather than 'containerForall'.
--
containerForall :: ContainerClass self => self
 -> ContainerForeachCB -- ^ @callback@ - a callback
 -> IO ()
containerForall self fun = do
  fPtr <- mkContainerForeachFunc (\wPtr _ -> do
    w <- makeNewObject mkWidget (return wPtr)
    fun w)
  {# call container_forall #}
    (toContainer self)
    fPtr
    nullPtr
  freeHaskellFunPtr fPtr

-- | Returns the container's non-internal children. See 'containerForall' for
-- details on what constitutes an \"internal\" child.
--
containerGetChildren :: ContainerClass self => self
 -> IO [Widget]
containerGetChildren self = do
  glist <- {# call container_get_children #} (toContainer self)
  widgetPtrs <- fromGList glist
  mapM (makeNewObject mkWidget . return) widgetPtrs

-- | Give the focus to a specific child of the container.
--
containerSetFocusChild :: (ContainerClass self, WidgetClass child) => self
 -> child -- ^ @child@
 -> IO ()
containerSetFocusChild self child =
  {# call container_set_focus_child #}
    (toContainer self)
    (toWidget child)

-- | Sets a focus chain, overriding the one computed automatically by Gtk+.
--
-- In principle each widget in the chain should be a descendant of the
-- container, but this is not enforced by this method, since it's allowed to
-- set the focus chain before you pack the widgets, or have a widget in the
-- chain that isn't always packed. The necessary checks are done when the focus
-- chain is actually traversed.
--
containerSetFocusChain :: ContainerClass self => self
 -> [Widget] -- ^ @focusableWidgets@ - the new focus chain.
 -> IO ()
containerSetFocusChain self chain =
  withForeignPtrs (map unWidget chain) $ \wPtrs ->
  withGList wPtrs $ \glist ->
  {# call container_set_focus_chain #}
    (toContainer self)
    glist

-- | Retrieves the focus chain of the container, if one has been set
-- explicitly. If no focus chain has been explicitly set, Gtk+ computes the
-- focus chain based on the positions of the children. In that case the
-- function returns @Nothing@.
--
containerGetFocusChain :: ContainerClass self => self
 -> IO (Maybe [Widget])
containerGetFocusChain self =
  alloca $ \glistPtr -> do
  {# call container_get_focus_chain #}
    (toContainer self)
    glistPtr
  if glistPtr == nullPtr then return Nothing else liftM Just $ do
    glist <- peek glistPtr
    widgetPtrs <- fromGList glist
    mapM (makeNewObject mkWidget . return) widgetPtrs

-- | Removes a focus chain explicitly set with 'containerSetFocusChain'.
--
containerUnsetFocusChain :: ContainerClass self => self -> IO ()
containerUnsetFocusChain self =
  {# call container_unset_focus_chain #}
    (toContainer self)

-- | Hooks up an adjustment to focus handling in a container, so when a child
-- of the container is focused, the adjustment is scrolled to show that widget.
-- This function sets the vertical alignment. See
-- 'scrolledWindowGetVAdjustment' for a typical way of obtaining the adjustment
-- and 'containerSetFocusHAdjustment' for setting the horizontal adjustment.
--
-- The adjustments have to be in pixel units and in the same coordinate
-- system as the allocation for immediate children of the container.
--
containerSetFocusVAdjustment :: ContainerClass self => self
 -> Adjustment -- ^ @adjustment@ - an adjustment which should be adjusted when
               -- the focus is moved among the descendents of @container@
 -> IO ()
containerSetFocusVAdjustment self adjustment =
  {# call container_set_focus_vadjustment #}
    (toContainer self)
    adjustment

-- | Retrieves the vertical focus adjustment for the container. See
-- 'containerSetFocusVAdjustment'.
--
containerGetFocusVAdjustment :: ContainerClass self => self
 -> IO (Maybe Adjustment) -- ^ returns the vertical focus adjustment, or
                          -- @Nothing@ if none has been set.
containerGetFocusVAdjustment self =
  maybeNull (makeNewObject mkAdjustment) $
  {# call unsafe container_get_focus_vadjustment #}
    (toContainer self)

-- | Hooks up an adjustment to focus handling in a container, so when a child
-- of the container is focused, the adjustment is scrolled to show that widget.
-- This function sets the horizontal alignment. See
-- 'scrolledWindowGetHAdjustment' for a typical way of obtaining the adjustment
-- and 'containerSetFocusVAdjustment' for setting the vertical adjustment.
--
-- The adjustments have to be in pixel units and in the same coordinate
-- system as the allocation for immediate children of the container.
--
containerSetFocusHAdjustment :: ContainerClass self => self
 -> Adjustment -- ^ @adjustment@ - an adjustment which should be adjusted when
               -- the focus is moved among the descendents of @container@
 -> IO ()
containerSetFocusHAdjustment self adjustment =
  {# call container_set_focus_hadjustment #}
    (toContainer self)
    adjustment

-- | Retrieves the horizontal focus adjustment for the container. See
-- 'containerSetFocusHAdjustment'.
--
containerGetFocusHAdjustment :: ContainerClass self => self
 -> IO (Maybe Adjustment) -- ^ returns the horizontal focus adjustment, or
                          -- @Nothing@ if none has been set.
containerGetFocusHAdjustment self =
  maybeNull (makeNewObject mkAdjustment) $
  {# call unsafe container_get_focus_hadjustment #}
    (toContainer self)

-- | Make the container resize its children.
--
containerResizeChildren :: ContainerClass self => self -> IO ()
containerResizeChildren self =
  {# call container_resize_children #}
    (toContainer self)

-- | Sets the border width of the container.
--
-- The border width of a container is the amount of space to leave around
-- the outside of the container. The only exception to this is 'Window';
-- because toplevel windows can't leave space outside, they leave the space
-- inside. The border is added on all sides of the container. To add space to
-- only one side, one approach is to create a 'Alignment' widget, call
-- 'widgetSetSizeRequest' to give it a size, and place it on the side of the
-- container as a spacer.
--
containerSetBorderWidth :: ContainerClass self => self
 -> Int   -- ^ @borderWidth@ - amount of blank space to leave /outside/ the
          -- container. Valid values are in the range 0-65535 pixels.
 -> IO ()
containerSetBorderWidth self borderWidth =
  {# call container_set_border_width #}
    (toContainer self)
    (fromIntegral borderWidth)

-- | Retrieves the border width of the container. See
-- 'containerSetBorderWidth'.
--
containerGetBorderWidth :: ContainerClass self => self
 -> IO Int -- ^ returns the current border width
containerGetBorderWidth self =
  liftM fromIntegral $
  {# call unsafe container_get_border_width #}
    (toContainer self)

-- | Returns the resize mode for the container. See 'containerSetResizeMode'.
--
containerGetResizeMode :: ContainerClass self => self
 -> IO ResizeMode -- ^ returns the current resize mode
containerGetResizeMode self =
  liftM (toEnum . fromIntegral) $
  {# call gtk_container_get_resize_mode #}
    (toContainer self)

-- | Sets the resize mode for the container.
--
-- The resize mode of a container determines whether a resize request will
-- be passed to the container's parent, queued for later execution or executed
-- immediately.
--
containerSetResizeMode :: ContainerClass self => self
 -> ResizeMode -- ^ @resizeMode@ - the new resize mode.
 -> IO ()
containerSetResizeMode self resizeMode =
  {# call gtk_container_set_resize_mode #}
    (toContainer self)
    ((fromIntegral . fromEnum) resizeMode)

--------------------
-- Attributes

-- | Specify how resize events are handled.
--
-- Default value: 'ResizeParent'
--
containerResizeMode :: ContainerClass self => Attr self ResizeMode
containerResizeMode = newAttr
  containerGetResizeMode
  containerSetResizeMode

-- | The width of the empty border outside the containers children.
--
-- Allowed values: \<= @('maxBound' :: Int)@
--
-- Default value: 0
--
containerBorderWidth :: ContainerClass self => Attr self Int
containerBorderWidth = newAttr
  containerGetBorderWidth
  containerSetBorderWidth

-- | Can be used to add a new child to the container.
--
containerChild :: (ContainerClass self, WidgetClass widget) => WriteAttr self widget
containerChild = writeAttrFromObjectProperty "child"
  {# call pure unsafe gtk_widget_get_type #}

-- | \'focusHadjustment\' property. See 'containerGetFocusHAdjustment' and
-- 'containerSetFocusHAdjustment'
--
containerFocusHAdjustment :: ContainerClass self => ReadWriteAttr self (Maybe Adjustment) Adjustment
containerFocusHAdjustment = newAttr
  containerGetFocusHAdjustment
  containerSetFocusHAdjustment

-- | \'focusVadjustment\' property. See 'containerGetFocusVAdjustment' and
-- 'containerSetFocusVAdjustment'
--
containerFocusVAdjustment :: ContainerClass self => ReadWriteAttr self (Maybe Adjustment) Adjustment
containerFocusVAdjustment = newAttr
  containerGetFocusVAdjustment
  containerSetFocusVAdjustment

--------------------
-- Signals

-- %hash c:26b d:af3f
-- | A widget was added to the container.
--
add :: ContainerClass self => Signal self (Widget -> IO ())
add = Signal (connect_OBJECT__NONE "add")

-- %hash c:f43a d:af3f
-- | A widget was removed from the container.
--
remove :: ContainerClass self => Signal self (Widget -> IO ())
remove = Signal (connect_OBJECT__NONE "remove")

-- %hash c:21a9 d:af3f
-- | Emitted when widgets need to be queried again for their preferred size.
--
checkResize :: ContainerClass self => Signal self (IO ())
checkResize = Signal (connect_NONE__NONE "check-resize")

-- %hash c:b3a d:af3f
-- | A widget in the container received or lost the input focus.
--
setFocusChild :: ContainerClass self => Signal self (Maybe Widget -> IO ())
setFocusChild = Signal (connect_MOBJECT__NONE "set-focus-child")

--------------------
-- Deprecated Signals

#ifndef DISABLE_DEPRECATED
-- %hash c:fb37
onAdd :: ContainerClass self => self
 -> (Widget -> IO ())
 -> IO (ConnectId self)
onAdd = connect_OBJECT__NONE "add" False
{-# DEPRECATED onAdd "instead of 'onAdd obj' use 'on obj add'" #-}

-- %hash c:c9d6
afterAdd :: ContainerClass self => self
 -> (Widget -> IO ())
 -> IO (ConnectId self)
afterAdd = connect_OBJECT__NONE "add" True
{-# DEPRECATED afterAdd "instead of 'afterAdd obj' use 'after obj add'" #-}

-- %hash c:9b66
onRemove :: ContainerClass self => self
 -> (Widget -> IO ())
 -> IO (ConnectId self)
onRemove = connect_OBJECT__NONE "remove" False
{-# DEPRECATED onRemove "instead of 'onRemove obj' use 'on obj remove'" #-}

-- %hash c:f165
afterRemove :: ContainerClass self => self
 -> (Widget -> IO ())
 -> IO (ConnectId self)
afterRemove = connect_OBJECT__NONE "remove" True
{-# DEPRECATED afterRemove "instead of 'afterRemove obj' use 'after obj remove'" #-}

-- %hash c:8424
onCheckResize :: ContainerClass self => self
 -> IO ()
 -> IO (ConnectId self)
onCheckResize = connect_NONE__NONE "check_resize" False
{-# DEPRECATED onCheckResize "instead of 'onCheckResize obj' use 'on obj checkResize'" #-}

-- %hash c:6803
afterCheckResize :: ContainerClass self => self
 -> IO ()
 -> IO (ConnectId self)
afterCheckResize = connect_NONE__NONE "check_resize" True
{-# DEPRECATED afterCheckResize "instead of 'afterCheckResize obj' use 'after obj checkResize'" #-}

-- %hash c:1ac6
onSetFocusChild :: ContainerClass self => self
 -> (Maybe Widget -> IO ())
 -> IO (ConnectId self)
onSetFocusChild = connect_MOBJECT__NONE "set-focus-child" False
{-# DEPRECATED onSetFocusChild "instead of 'onSetFocusChild obj' use 'on obj setFocusChild'" #-}

-- %hash c:23e5
afterSetFocusChild :: ContainerClass self => self
 -> (Maybe Widget -> IO ())
 -> IO (ConnectId self)
afterSetFocusChild = connect_MOBJECT__NONE "set-focus-child" True
{-# DEPRECATED afterSetFocusChild "instead of 'afterSetFocusChild obj' use 'after obj setFocusChild'" #-}
#endif
