#
# $Id: menubar.icn,v 1.4 2006-07-09 23:43:07 rparlett Exp $
#
# This file is in the public domain.
#
# Author: Robert Parlett (parlett@dial.pipex.com)
#

package gui
link graphics

$include "guih.icn"


#
# This class is the base from which menu systems are created,
# other than popup menus.
#
# Menu items are added to this class; they are not separate
# components added to the dialog itself.
#
# The default position is (0, 0); the default size is 100% of
# the width of the screen and a reasonable height based on the
# font specified.
#
class MenuBar : Component(
   which_highlight,      #
   which_open,           #
   menus                 #
   )

   #
   # Add the {Menu} c to the {MenuBar}.  This will be one drop down
   # menu.  Items are then added to the {Menu}.
   # @param c  The {Menu} to add.
   #
   method add(c)
      put(self.menus, c)
   end

   method finally()
      #
      # Disposing whilst active - close.
      #
      if \self.which_highlight then {
         self.set_which_highlight()
         self.unique_end()
      }
      self.Component.finally()
   end

   method display(buffer_flag)
      local m
      EraseRectangle(cbwin, x, y, w, h)
      DrawRaisedRectangle(cbwin, x, y, w, h)

      #
      # Draw the menu options with a raised rectangle around the open menu.
      # m.label_mid_w gives the space for the menu label, which includes
      # DEFAULT_TEXT_X_SURROUND either side of the label itself.  m.label_x
      # is the x position, so m.label_x + DEFAULT_TEXT_X_SURROUND put the
      # string in the centre of its area.
      #
      every m := !menus do {
         if m === \which_highlight then
            DrawRaisedRectangle(cbwin, m.label_x, y + BORDER_WIDTH,
                                m.label_mid_w, h - 2 * BORDER_WIDTH)
         left_string(cbwin, m.label_x + DEFAULT_TEXT_X_SURROUND, y + h / 2,
                     m.get_label(), m.get_accel())
         if \m.is_shaded_flag then
            FilterRectangle(cbwin, m.label_x, y + BORDER_WIDTH,
                            m.label_mid_w, h - 2 * BORDER_WIDTH)
      }
      do_shading(cbwin)

      if /buffer_flag then
         CopyArea(cbwin, cwin, x, y, w, h, x, y)
   end

   #
   # Determine which of the menu labels is selected, if any.
   # Assumes y pos already tested and in menu bar.
   #
   # @p
   method which_button()
      local m
      every m := !self.menus do {
         if /m.is_shaded_flag & m.label_x <= &x < m.label_x + m.label_mid_w then
            return m
      }
   end

   #
   # Set the present open menu to x.  If x null, no menu open.
   #
   # @p
   method set_which_open(x)
      #
      # Do nothing if x presently open
      #
      if (self.which_open ~=== x) | (self.which_highlight ~=== x) then {
         # Hide any existing visible submenu
         (\self.which_open).hide()
         self.which_highlight := self.which_open := x
         # If it's visible, display it
         (\self.which_open).display()
         self.invalidate()
      }
      return x
   end

   #
   # Set the present highlight to x.
   #
   # @p
   method set_which_highlight(x)
      #
      # Do nothing if x presently open
      #
      if (self.which_highlight ~=== x) | \self.which_open then {
         # Hide any existing visible submenu
         (\self.which_open).hide()
         self.which_highlight := x
         self.which_open := &null
         self.invalidate()
      }
      return x
   end

   method handle_key_do(e)
      local m

      if \self.which_highlight then {
         self.set_which_highlight()
         self.unique_end(1)
         return
      }

      every m := !menus do {
         if not(m.is_shaded()) then {
            self.unique_start()
            set_which_highlight(m)
            return
         }
      }
   end

   method handle_key_left(e)
      if \self.which_open then
         self.which_highlight.handle_event(e)
      else if \self.which_highlight then
         go_left()
   end

   method handle_key_right(e)
      if \self.which_open then
         self.which_highlight.handle_event(e)
      else if \self.which_highlight then
         go_right()
   end

   method handle_key_down(e)
      if \self.which_open then
         self.which_highlight.handle_event(e)
      else if \self.which_highlight then {
         set_which_open(self.which_highlight)
         self.which_highlight.cursor_on()
      }
   end

   method make_partial()
      set_which_highlight(self.which_highlight)
   end

   method handle_key_escape(e)
      if \self.which_open then
         self.which_highlight.handle_event(e)
      else if \self.which_highlight then {
         self.set_which_highlight()
         self.unique_end()
      }
   end

   method handle_default(e)
      local m

      if \self.which_open then
         self.which_highlight.handle_event(e)
      else if /self.which_highlight then {
         if &meta & m := find_key(e) then {
            self.unique_start()
            set_which_open(m)
            m.cursor_on()
         }
      } else {
         if m := find_key(e) then {
            set_which_open(m)
            m.cursor_on()
         }
      }
   end

   method find_key(k)
      local m
      every m := !menus do {
         if m.accel === k & not(m.is_shaded()) then
            return m
      }
   end

   method go_right()
      local m, t, first
      every m := !menus do {
         if not(m.is_shaded()) then {
            if /self.which_highlight | \t then {
               first := m
               break
            }
            /first := m
         }
         if m === which_highlight then
            t := m
      }
      if \first then {
         if \self.which_open then {
            set_which_open(first)
            self.which_open.cursor_on()
         } else
            set_which_highlight(first)
      }
   end

   method go_left()
      local m, last

      every m := !menus do {
         if \last & m === which_highlight then
            break
         m.is_shaded() | (last := m)
      }
      if \last then {
         if \self.which_open then {
            set_which_open(last)
            self.which_open.cursor_on()
         } else
            set_which_highlight(last)
      }
   end

   method handle_press(e)
      local t
      if self.in_region() then {
         if t := which_button() then {
            #
            # Pressed on a label - open the menu, starting unique mode if needed.
            #
            if /self.which_highlight then
               self.unique_start()

            #
            # Open t, possibly just toggling the partial_flag.
            #
            if (t === which_highlight) & \self.which_open then
               set_which_highlight(t)
            else
               set_which_open(t)
         } else {
            self.set_which_highlight()
            self.unique_end(1)
         }
      } else {
         if \self.which_open then
            which_highlight.handle_event(e)
         else {
            #
            # Clicked outside the region with no submenu - close.
            #
            self.set_which_highlight()
            self.unique_end(1)
         }
      }
   end

   method handle_release(e)
      local t
      if self.in_region() & \self.which_highlight then {
         #
         # Released with menu open.  If not on a label then close
         #
         if not(t := which_button()) then {
            self.set_which_highlight()
            self.unique_end(1)
         }
      } else {
         (\self.which_open).handle_event(e)
      }
   end

   method handle_drag(e)
      local t
      if \self.which_highlight then {
         if self.in_region() then {
            #
            # Drag onto a label with menu open
            #
            if t := which_button() then {
               if t === self.which_open then
                  #
                  # Make present menu blank, except a sub menu
                  #
                  t.drag_off()
               else {
                  #
                  # Open the menu, maintaining the partial_flag
                  #
                  if /self.which_open then
                     self.set_which_highlight(t)
                  else
                     self.set_which_open(t)
               }
            } else
               #
               # Drag with menu open, but not on a label, blank present menu leaving
               # sub menus open
               #
               (\self.which_open).drag_off()
         } else if \self.which_open then
            self.which_open.handle_event(e)
         else if e = -12 then {
            self.set_which_highlight()
            self.unique_end()
         }
      }
   end

   method handle_event(e)
      if e === (&lpress | &rpress | &mpress) then {
         handle_press(e)
      } else if e === (&lrelease | &rrelease | &mrelease) then {
         handle_release(e)
      } else if e === (-12 | &ldrag | &rdrag | &mdrag) then {
         handle_drag(e)
      } else {
         case e of {
            Key_Down | " " | "\r": handle_key_down(e)
            Key_Right: handle_key_right(e)
            Key_Left: handle_key_left(e)
            Key_Do: handle_key_do(e)
            "\e" : handle_key_escape(e)
            default: handle_default(e)
         }
      }
   end

   method resize()
      local px, m
      #
      # Re-sized with menu open - just close menu
      #
      if \self.which_highlight then {
         self.set_which_highlight()
         self.unique_end()
      }

      /self.x_spec := 0
      /self.y_spec := 0
      /self.w_spec := "100%"
      /self.h_spec := WAttrib(self.cwin, "fheight") +  2 * DEFAULT_TEXT_Y_SURROUND
      self.Component.resize()
      #
      # Compute x, y for each sub-menu
      #
      px := self.x + BORDER_WIDTH
      every m := !self.menus do {
         m.set_parent_component(self)
         m.set_abs_coords(px, self.y + self.h)
         m.set_label_pos(px, self.y + BORDER_WIDTH)
         m.size_label()
         px +:= m.label_mid_w
         m.resize()
      }
   end

   initially(a[])
      self.Component.initially()
      self.menus := []
      set_fields(a)
end
