-- | HTkMenu is a user-friendly interface to HTk's menu operations, which
-- compiles a version of MenuType.MenuPrim to an HTk menu.
module HTk.Toolkit.HTkMenu(
   ) where

import Util.Computation

import Events.Events

import HTk.Kernel.Core(HasCommand(..))
import HTk.Kernel.Packer(Container)
import HTk.Kernel.Configuration(HasText(..))

import HTk.Menuitems.Menu
import HTk.Widgets.MenuButton
import HTk.Menuitems.MenuCascade
import HTk.Menuitems.MenuSeparator
import HTk.Menuitems.MenuCommand

import HTk.Toolkit.MenuType hiding (MenuPrim(Menu))
import qualified HTk.Toolkit.MenuType as MenuType (MenuPrim(Menu))

-- ----------------------------------------------------------------------
-- The HTkMenu type
-- ----------------------------------------------------------------------

-- | Describes a menu to be compiled.
-- The value identifies the buttons in the menu so the client
-- can tell which was clicked.
-- The String is a title which is given to menu cascades.
newtype HTkMenu value = HTkMenu (MenuType.MenuPrim String value)

-- ----------------------------------------------------------------------
-- compileHTkMenu
-- ----------------------------------------------------------------------

-- | compileHTkMenu compiles a menu to a MenuButton.  It does not display it;
-- the caller should pack the MenuButton in the parent with whatever options
-- are desired.
compileHTkMenu :: Container parent => parent -> HTkMenu value
   -> IO (MenuButton,Event value)
compileHTkMenu parent htkMenu =
      let (title,subMenus) = normalise htkMenu
      menuButton <- newMenuButton parent [text title]
      topMenu <- createMenu menuButton tearoff []
      menuButton # menu topMenu
      clickEvents <- mapM (compileMenuPrim topMenu) subMenus
      return (menuButton,choose clickEvents)

-- | normalise decomposes the menu into a title plus a list of submenus.
normalise :: HTkMenu value -> (String,[MenuType.MenuPrim String value])
normalise (HTkMenu menuPrim) =
   case menuPrim of
      MenuType.Menu title subMenus -> (title,subMenus)
      Button s value -> (s,[menuPrim])
      Blank -> ("",[Blank])

-- | Set tearoff if we want tearoff menus, which means ones which open a
-- new top-level window.
tearoff :: Bool
tearoff = False

-- | Compiles the menu and inserts it into the parent (which is itself a menu),
-- returning the event click operation
compileMenuPrim :: Menu -> MenuType.MenuPrim String value -> IO (Event value)
compileMenuPrim parent menuPrim =
   case menuPrim of
      Button string value ->
            menuCommand <- createMenuCommand parent [text string]
            event <- clicked menuCommand
            return (event >> return value)
      Blank ->
            menuSeparator <- createMenuSeparator parent []
            return never
      MenuType.Menu title subMenuPrims ->
            cascade <- createMenuCascade parent [text title]
            innerMenu <- createMenu parent tearoff []
            cascade # menu innerMenu
            events <- mapM (compileMenuPrim innerMenu) subMenuPrims
            return (choose events)