menu_window.cpp
上传用户:yhdzpy8989
上传日期:2007-06-13
资源大小:13604k
文件大小:49k
源码类别:

生物技术

开发平台:

C/C++

  1. /*
  2.  * ===========================================================================
  3.  * PRODUCTION $Log: menu_window.cpp,v $
  4.  * PRODUCTION Revision 1000.0  2004/06/01 21:29:21  gouriano
  5.  * PRODUCTION PRODUCTION: IMPORTED [GCC34_MSVC7] Dev-tree R1.7
  6.  * PRODUCTION
  7.  * ===========================================================================
  8.  */
  9. /*  $Id: menu_window.cpp,v 1000.0 2004/06/01 21:29:21 gouriano Exp $
  10.  * ===========================================================================
  11.  *
  12.  *                            PUBLIC DOMAIN NOTICE
  13.  *               National Center for Biotechnology Information
  14.  *
  15.  *  This software/database is a "United States Government Work" under the
  16.  *  terms of the United States Copyright Act.  It was written as part of
  17.  *  the author's official duties as a United States Government employee and
  18.  *  thus cannot be copyrighted.  This software/database is freely available
  19.  *  to the public for use. The National Library of Medicine and the U.S.
  20.  *  Government have not placed any restriction on its use or reproduction.
  21.  *
  22.  *  Although all reasonable efforts have been taken to ensure the accuracy
  23.  *  and reliability of the software and data, the NLM and the U.S.
  24.  *  Government do not and cannot warrant the performance or results that
  25.  *  may be obtained by using this software or data. The NLM and the U.S.
  26.  *  Government disclaim all warranties, express or implied, including
  27.  *  warranties of performance, merchantability or fitness for any particular
  28.  *  purpose.
  29.  *
  30.  *  Please cite the author in any work or product based on this material.
  31.  *
  32.  * ===========================================================================
  33.  *
  34.  * Authors:  Andrey Yazhuk
  35.  *  
  36.  */
  37. #include <ncbi_pch.hpp>
  38. #include <corelib/ncbistd.hpp>
  39. #include <algorithm>
  40. #include <gui/utils/accel_table.hpp>
  41. #include <gui/widgets/fl/menu_window.hpp>
  42. #include <FL/fl_draw.H>
  43. #include <FL/Fl_Image.H>
  44. #include <FL/Enumerations.H>
  45. #include <FL/Fl.H>
  46. #include <math.h>
  47. BEGIN_NCBI_SCOPE
  48. ///////////////////////////////////////////////////////////////////////////////
  49. /// CMenuProperties
  50. CMenu::CProperties::CProperties()
  51. {
  52.     m_FontType = FL_HELVETICA;
  53.     m_FontSize = 12;
  54.     m_Colors[eBack] = fl_rgb_color(216, 216, 216);
  55.     m_Colors[eFocusedBack] = fl_rgb_color(192, 192, 216);
  56.     m_Colors[eCheckedBack] = fl_rgb_color(200, 200, 216);
  57.     m_Colors[ePushedBack] = fl_rgb_color(128, 128, 192);
  58.     m_Colors[ePopupBack] = fl_rgb_color(248, 248, 255);
  59.     m_Colors[eFrame] = fl_rgb_color(64, 64, 64);
  60.     m_Colors[eText] = fl_rgb_color(0, 0, 0);
  61.     m_Colors[eDisabledText] = fl_rgb_color(196, 196, 196);
  62.     m_Colors[eFocusedDisabledText] = fl_rgb_color(224, 224, 224);
  63.     m_Colors[ePushedText] = fl_rgb_color(255, 255, 255);
  64.     m_Colors[eFocusedFrame] = fl_rgb_color(0, 0, 128);
  65.     m_Colors[eIconArea] = fl_rgb_color(232, 232, 240);
  66.     m_Colors[eBorder] = fl_rgb_color(255, 255, 255);
  67. }
  68. const static int    kDefaultIconSize = 16;
  69. const static int    kCheckSize = 16;
  70. const static int    kRadioSize = 16;
  71. const static int    kSubMarkerH = 7; // submenu marker height 
  72. const static int    kSubMarkerOffset = 7; // submenu marker offset from the right
  73. const static int    kSubMarkerW = 12; // submenu marker width
  74. const static int    kBorderSize = 3; // menu border
  75. const static int    kSpace = 2; // spacing around icons, labels
  76. const static int    kAccelSpace = 8; // spacing between item label and accelerator label
  77. const static int    kItemOffsetY = 3;
  78. const static int    kItemOffsetX = 4; // horz spacing between items  
  79. const static int    kSubmenuOffsetX = 8; // horz spacing between submenus
  80. const static int    kSeparatorSize = 1 + 2 * kItemOffsetX;
  81. const static char   kAccessKeyMarker = '&';
  82. const static double kMenuDelay = 0.4;
  83. ///////////////////////////////////////////////////////////////////////////////
  84. /// class CMenu - base class for all menus
  85. CMenu::CProperties  CMenu::sm_Props;
  86. CRef<CResourceManager>   CMenu::sm_ResManager; 
  87. CRef<CResourceManager>   CMenu::GetResourceManager(void)
  88. {
  89.     return sm_ResManager;
  90. }
  91. CMenu::TCmdToHintMap   CMenu::sm_CmdToHint;
  92. void    CMenu::SetResourceManager(CRef<CResourceManager> manager)
  93. {
  94.     sm_ResManager = manager;
  95.     if(sm_ResManager)   {
  96.         manager->RegisterAlias("menu::check", "check.png");
  97.         manager->RegisterAlias("menu::radio", "radio.png");    
  98.     }
  99. }
  100. void    CMenu::SetCmdHint(TCmdID cmd, const string& hint)
  101. {
  102.     sm_CmdToHint[cmd] = hint;
  103. }
  104. string   CMenu::GetCmdHint(TCmdID cmd)
  105. {
  106.     TCmdToHintMap::iterator it = sm_CmdToHint.find(cmd);
  107.     return (it != sm_CmdToHint.end()) ? it->second : "";
  108. }
  109. CMenu::CMenu()
  110. :   m_RootItem(NULL),
  111.     m_bOwnItems(false),
  112.     m_CmdTarget(NULL),
  113.     m_Listener(NULL),
  114.     m_Width(0), 
  115.     m_Height(0),
  116.     m_Border(0),
  117.     m_IconSize(kDefaultIconSize),
  118.     m_MaxLabelW(0),
  119.     m_BackColor(eBack),
  120.     m_Selected(NULL),
  121.     m_PushedItem(NULL),
  122.     m_bKeyActivated(false)
  123. {
  124. }
  125. CMenu::~CMenu()
  126. {
  127.     x_Clear();
  128. }
  129. void    CMenu::SetCmdTarget(CCommandTarget* target)
  130. {
  131.     m_CmdTarget = target;
  132. }
  133. CCommandTarget*    CMenu::GetCmdTarget(void)
  134. {
  135.     return m_CmdTarget;
  136. }
  137. void    CMenu::SetItemImageSize(int size)
  138. {
  139.     _ASSERT(size >= 0  && size < 100);
  140.     m_IconSize = size;
  141. }
  142. bool    CMenu::x_IsKeyActivated() const
  143. {
  144.     return m_bKeyActivated;
  145. }
  146. /// Creates a menu described by array of SMenuItem-s. CMenuBar creates a separate
  147. /// copy of a menu, so that it can be modified at run-time.
  148. void    CMenu::SetItems(const SMenuItemRec* items)
  149. {
  150.     CMenuItem* root = CreateMenuItems(items);
  151.     x_SetItems(root, true);
  152. }
  153. void    CMenu::SetItems(CMenuItem* root_item)
  154. {    
  155.     x_SetItems(root_item, true);
  156. }
  157. void    CMenu::SetHintListener(IMenuHintListener* listener)
  158. {
  159.     m_Listener = listener;
  160. }
  161. IMenuHintListener*  CMenu::GetHintListener()
  162. {
  163.     return m_Listener;
  164. }
  165. void    CMenu::x_SetItems(CMenuItem* root_item, bool take_ownership)
  166. {
  167.     x_Clear();
  168.     m_RootItem = root_item;
  169.     m_bOwnItems = take_ownership;
  170.     
  171.     if(m_RootItem)  {
  172.         CResourceManager* manager = GetResourceManager();
  173.         _ASSERT(manager);
  174.         m_hCheckIcon = manager->GetImage("menu::check");
  175.         m_hRadioIcon = manager->GetImage("menu::radio");
  176.         for(CMenuItem::TChildItem_CI it =  m_RootItem->SubItemsBegin();
  177.                                         it != m_RootItem->SubItemsEnd(); ++it) {
  178.             CMenuItem* item = (*it)->GetValue();
  179.             
  180.             // create entry for a menu item
  181.             SItemEntry entry;
  182.             entry.m_Item = item;
  183.             if(item->HasImage())    {
  184.                 entry.m_hIcon = manager->GetImage(item->GetImageAlias()); // preloading images
  185.             }
  186.             entry.m_Size = 0;
  187.             // create access key entry
  188.             const string& label = item->GetLabel();
  189.             string::size_type pos = label.find(kAccessKeyMarker);
  190.             if(pos != string::npos  &&  pos + 1 < label.size()) {
  191.                 char ch = label[pos + 1];
  192.                 ch = tolower(ch);
  193.                 
  194.                 TAccessKeyToItemMap::const_iterator it = m_AccessKeyToItem.find(ch);
  195.                 if(it == m_AccessKeyToItem.end())   { // OK
  196.                     m_AccessKeyToItem[ch] = item;
  197.                 }   else {
  198.                     ERR_POST("CMenuItem "" << label << "" specifies accelerator that is already defined in this menu.");
  199.                 }            
  200.             }
  201.         
  202.             // lookup item accelerator
  203.             if(item->IsItem())  {
  204.                 TCmdID cmd = item->GetCommand();
  205.                 int accel = 0;
  206.                 if(CAccelTable::GetAccelByCommand(cmd, accel))    {
  207.                     entry.m_AccelLabel = CAccelTable::GetAccelLabel(accel);
  208.                 }
  209.             }
  210.             m_Entries.push_back(entry);
  211.         }
  212.     }
  213.     
  214.     x_Layout();    
  215. }
  216. void    CMenu::x_Clear(void)
  217. {
  218.     m_AccessKeyToItem.clear();
  219.     m_Entries.clear();
  220.     m_Selected = NULL;
  221.     
  222.     if(m_bOwnItems)   {
  223.         delete m_RootItem;
  224.     }
  225.     m_RootItem = NULL;
  226. }
  227. void    CMenu::x_UpdateItems(void)
  228. {
  229.     CCommandTarget* target = GetCmdTarget();
  230.     if(target)  {
  231.         NON_CONST_ITERATE(TEntries, it, m_Entries)  {
  232.             CMenuItem& item = *it->m_Item;
  233.             if(item.IsItem())   {
  234.                 CMenuCmdUI CmdUI(item);
  235.                 
  236.                 target->OnUpdateCommand(item.GetCommand(), &CmdUI);
  237.             }
  238.         }
  239.     }
  240. }
  241. // all coordinates calculated are relative to widget's origin
  242. void    CMenu::x_Layout(void)
  243. {
  244.     m_Width = 0;
  245.     m_Height = 0;
  246.     m_MaxLabelW = 0;
  247.     int text_w = 0;
  248.     int marker_w = 0;
  249.     
  250.     fl_font(sm_Props.m_FontType, sm_Props.m_FontSize);
  251.     const int item_h = max(m_IconSize, fl_height()) + kItemOffsetY * 2;
  252.     bool b_horz = x_IsHorizontal();
  253.     int i = 0; // iterate by items
  254.     ITERATE(TEntries, it, m_Entries)  {
  255.         const CMenuItem& item = *it->m_Item;
  256.                 
  257.         x_MeasureItem(*it, text_w); // measure text length
  258.         
  259.         if(b_horz) { 
  260.             int icon_space = m_Entries[i].m_hIcon ? 
  261.                             (m_IconSize + kSpace) : 0;
  262.             int w = item.IsSeparator() ? kSeparatorSize : 
  263.                     (icon_space + text_w + 2 * (item.IsItem() ? kItemOffsetX : kSubmenuOffsetX));
  264.             m_Entries[i].m_Size = w;  
  265.             m_Width += w;
  266.         } else  { // vertical orientation
  267.             int h = 0;
  268.             if(item.IsSeparator())  {
  269.                 h = kSeparatorSize;
  270.             } else {
  271.                 if(item.IsSubmenu())    {
  272.                     marker_w = kSubMarkerW;
  273.                 }
  274.                 m_MaxLabelW = max(m_MaxLabelW, text_w);
  275.                 h = item_h;
  276.             }            
  277.             m_Entries[i].m_Size = h;  
  278.             m_Height += h;          
  279.         }
  280.         i++;
  281.     }
  282.     if(b_horz)  {
  283.         m_Height = item_h;
  284.     } else {
  285.         m_Width = (m_IconSize + 2 * kSpace) + kSpace + m_MaxLabelW
  286.                   + (marker_w + 2 * kSpace) + 2 * kItemOffsetX;
  287.     }
  288.     m_Width += 2 * m_Border;
  289.     m_Height += 2 * m_Border;
  290. }
  291. CPoint    CMenu::GetPreferredSize(void) const
  292. {
  293.     return CPoint(m_Width, m_Height);
  294. }
  295. void    CMenu::SetSelected(CMenuItem* item)
  296. {
  297.     if(m_Selected != item)   {
  298.         m_Selected = item;
  299.         GetWidget().damage(FL_DAMAGE_OVERLAY);//redraw();
  300.     }
  301. }
  302. CMenuItem*      CMenu::GetRootItem(void)
  303. {
  304.     return m_RootItem;
  305. }
  306. int     CMenu::GetBorder(void) const
  307. {
  308.     return m_Border;
  309. }
  310. void    CMenu::x_Draw(int x, int y)
  311. {    
  312.     Fl_Widget& wid = GetWidget();
  313.     int w = wid.w(), h = wid.h();
  314.     fl_clip(x, y, w, h);
  315.     if(m_Border > 0)    { // draw frame and border
  316.         fl_color(sm_Props.GetColor(eFrame));
  317.         fl_rect(x, y, w, h); // frame
  318.         fl_color(sm_Props.GetColor(eBorder));
  319.         for( int i = 1; i < m_Border; i++ ) { // border
  320.             fl_rect(i, i, w - 2 * i, h - 2 * i); 
  321.         }
  322.     }
  323.     // fill background not filled by items
  324.     fl_color(sm_Props.GetColor(m_BackColor));
  325.     // fill the right part
  326.     int x_off = m_Width - m_Border;
  327.     fl_rectf(x + x_off, y + m_Border, w - m_Border  - x_off, h - 2 * m_Border);
  328.     // fill the bottom part
  329.     int y_off = m_Height - m_Border;
  330.     fl_rectf(x + m_Border, y + y_off, m_Width - 2 * m_Border, h - m_Border - y_off);
  331.     x_DrawItems(x + m_Border, y + m_Border);
  332.     fl_pop_clip();
  333. }
  334. void    CMenu::x_DrawItems(int x, int y)    
  335. {
  336.     bool b_horz = x_IsHorizontal();
  337.     if(m_RootItem) {
  338.         fl_font(sm_Props.m_FontType, sm_Props.m_FontSize);
  339.         int  i = 0;
  340.         NON_CONST_ITERATE(TEntries, it, m_Entries)    {
  341.             int size = it->m_Size;                        
  342.             if(b_horz)  {
  343.                 x_DrawItem(*it, x, y, size, m_Height - 2 * m_Border);
  344.                 x += size;
  345.             } else {
  346.                 x_DrawItem(*it, x, y, m_Width - 2 * m_Border, size);
  347.                 y += size;
  348.             }
  349.             i++;
  350.         }
  351.     }
  352. }
  353. /// draw single menu item in the given rectangular area
  354. void    CMenu::x_DrawItem(CMenu::SItemEntry& entry, int x, int y, int w, int h)
  355. {
  356.     CMenuItem& item = *entry.m_Item;
  357.     
  358.     bool b_icon = (bool)(entry.m_hIcon);
  359.     int icon_w = x_IsHorizontal() ? (m_IconSize + kSpace) 
  360.                                   : (m_IconSize + 2 * kSpace);
  361.     bool b_focused = false;
  362.     bool b_pushed = false;
  363.     if(x_IsSubMenuPushed()) {
  364.         b_focused = (m_Selected == &item)  &&  item.IsEnabled();
  365.     } else if(x_IsItemPushed())   {
  366.         b_focused = (m_Selected != m_PushedItem)  &&  (m_PushedItem == &item);
  367.         b_pushed = (m_Selected == m_PushedItem)  && (m_PushedItem == &item);
  368.     } else {
  369.         b_focused = (m_Selected == &item)  &&  
  370.             (! x_IsHorizontal()  ||  Fl::belowmouse() == &GetWidget() ||  x_IsKeyActivated());
  371.     }
  372.     bool b_check = item.IsChecked()  ||  item.IsRadioSelected(); // item checked
  373.     
  374.     Fl_Color back_color = FL_RED; 
  375.     if(b_focused  ||  b_pushed)   { // draw focused item with frame        
  376.         EColorType color = (x_IsHorizontal()  &&  item.IsSubmenu())
  377.                             ? (m_PushedItem ? ePopupBack : eFocusedBack)
  378.                             : (b_pushed ? ePushedBack : eFocusedBack);
  379.         back_color = sm_Props.GetColor(color);
  380.         fl_color(back_color);
  381.         fl_rectf(x, y, w, h);
  382.         // draw focused frame
  383.         fl_color(sm_Props.GetColor(x_IsItemPushed() ? eFocusedFrame : eFrame));
  384.         fl_rect(x, y, w, h);
  385.     } else { // draw unfocused item        
  386.         back_color = sm_Props.GetColor(m_BackColor);
  387.         if(x_IsHorizontal())    {    
  388.             if(b_check)    {
  389.                 fl_color(sm_Props.GetColor(eCheckedBack));
  390.                 fl_rectf(x, y, w, h);
  391.                 fl_color(sm_Props.GetColor(eFocusedFrame));
  392.                 fl_rect(x, y, w, h);
  393.             } else {                
  394.                 fl_color(back_color);
  395.                 fl_rectf(x, y, w, h);
  396.             }            
  397.         } else {            
  398.             fl_color(back_color);
  399.             fl_rectf(x + icon_w, y, w - icon_w, h); // fill text background
  400.             back_color = sm_Props.GetColor(eIconArea);
  401.             fl_color(back_color);            
  402.             fl_rectf(x, y, icon_w, h); // draw icon placeholder
  403.         } 
  404.     }
  405.     int y_c = y + h / 2;
  406.             
  407.     if(item.IsSeparator())  { //draw separation line        
  408.         fl_color(sm_Props.GetColor(eDisabledText));    
  409.         if(x_IsHorizontal())    {
  410.             int x_c = x + w / 2;
  411.             fl_line(x_c, y + 1, x_c, y + h - 2); // horz line
  412.         } else {
  413.             fl_line(x + icon_w + 1, y_c, x + w - 2, y_c); // vert line
  414.         }
  415.     } else {  
  416.         // draw Icon
  417.         int x_off = x_IsHorizontal() ? (item.IsItem() ? kItemOffsetX : kSubmenuOffsetX)
  418.                                      : kSpace;
  419.         x_DrawItemIcon(entry, x + x_off, y_c - m_IconSize / 2, back_color);         
  420.         // draw Label adn Accelerator Label
  421.         EColorType text_color = b_focused ? eFocusedDisabledText : eDisabledText;
  422.         if(item.IsEnabled())    {
  423.             text_color = b_pushed ? ePushedText : eText;
  424.         }
  425.         fl_color(sm_Props.GetColor(text_color));
  426.         int text_off = x_off;
  427.         if(x_IsHorizontal())    {
  428.             text_off += b_icon ? (icon_w + kSpace) : 0;
  429.         } else {
  430.             text_off = icon_w + kSpace * 2;
  431.         }    
  432.         x_DrawItemText(entry, x + text_off, y, w, h);      
  433.         
  434.         // draw submenu marker
  435.         if(! x_IsHorizontal()  &&  item.IsSubmenu())    { 
  436.             int N = kSubMarkerH / 2;
  437.             int m_x = x + (w - 1) - kSubMarkerOffset;
  438.             int y_c = y + h / 2;
  439.             
  440.             fl_color(sm_Props.GetColor(eText));
  441.             for( int i = 0; i <= N; i++ )    { // draw triangle as series of lines
  442.                 fl_line(m_x - i, y_c - i, m_x - i, y_c + i);
  443.             }
  444.         }
  445.     }
  446. }
  447. /// draws item's label and accelerator label (if any)
  448. void    CMenu::x_DrawItemText(const CMenu::SItemEntry& entry, int x, int y, int w, int h)
  449. {    
  450.     const CMenuItem& item = *entry.m_Item;
  451.     const string& text = item.GetLabel();
  452.     int text_x = x;
  453.     int y_c = y + h / 2;
  454.     int text_y = y_c + fl_height() / 2 - 2;
  455.    
  456.     int label_len = 0;
  457.     string::size_type pos = text.find(kAccessKeyMarker); // find accelerator marker
  458.     if(pos != string::npos  &&  pos + 1 < text.size())   { // there is an accelerator        
  459.         int line_x = text_x;
  460.         if(pos > 0) {
  461.             fl_draw(text.c_str(), pos, text_x, text_y); // draw all the text before marker
  462.             line_x += (int) ceil(fl_width(text.c_str(), pos));
  463.         }
  464.         
  465.         int line_len = (int) ceil(fl_width(text.c_str() + pos + 1, 1));
  466.         fl_draw(text.c_str() + pos + 1, line_x, text_y); // draw text after Marker
  467.         
  468.         int line_y = text_y + 2;
  469.         fl_line(line_x, line_y, line_x + line_len - 1, line_y); // draw Underscore
  470.     }
  471.     else { // no accelerator - draw plain text
  472.         fl_draw(text.c_str(), text_x, text_y);
  473.     }
  474.     // draw accelerator label
  475.     if(! x_IsHorizontal())  {
  476.         int len = (int) ceil(fl_width(entry.m_AccelLabel.c_str()));
  477.         fl_draw(entry.m_AccelLabel.c_str(), x + m_MaxLabelW - len, text_y);
  478.     }
  479. }
  480. void    CMenu::x_DrawItemIcon(CMenu::SItemEntry& entry, int x, int y, Fl_Color back_color)
  481. {
  482.     CMenuItem& item = *entry.m_Item;
  483.     CFLTKImageHandle hIcon;
  484.     if(item.IsChecked())    {
  485.         hIcon = m_hCheckIcon;
  486.     } else if(item.IsRadioSelected())    {
  487.         hIcon = m_hRadioIcon;
  488.     } else {
  489.         hIcon = entry.m_hIcon;            
  490.     }
  491.     if(hIcon) { //draw icon if any
  492.         Fl_Image* image = &(*hIcon);
  493.         bool disabled = ! item.IsEnabled();
  494.         CPNGImageExt* ext_image = dynamic_cast<CPNGImageExt*>(image);
  495.         if(ext_image)   {
  496.             ext_image->DrawTransparent(back_color, x, y, m_IconSize, m_IconSize, disabled);
  497.         } else {
  498.             if(disabled)    {
  499.                 image = image->copy();
  500.                 image->inactive();
  501.             }
  502.             image->draw(x, y, m_IconSize, m_IconSize);
  503.             if(disabled)    {
  504.                 delete image;
  505.             }
  506.         }
  507.     } 
  508. }
  509. void    CMenu::x_MeasureItem(const SItemEntry& entry, int& text_w) const
  510. {
  511.     CMenuItem& item = *entry.m_Item;
  512.     if(item.IsSeparator())  {
  513.         text_w = 0;
  514.     } else {
  515.         fl_font(sm_Props.m_FontType, sm_Props.m_FontSize);
  516.         const string& text = item.GetLabel();
  517.         text_w = (int) ceil(fl_width(text.c_str()));
  518.         string::size_type pos = text.find(kAccessKeyMarker); // find accelerator marker
  519.         if(pos != string::npos  &&  pos + 1 < text.size())   { // there is an accelerator        
  520.             text_w -= (int) ceil(fl_width("&"));
  521.         }
  522.         if(! x_IsHorizontal())  {
  523.             // measure accelerator label
  524.             text_w += kAccelSpace;
  525.             text_w += (int) ceil(fl_width(entry.m_AccelLabel.c_str()));
  526.         }
  527.     }
  528. }
  529. CRect    CMenu::x_GetPopupItemRect(const CMenuItem& r_item)
  530. {
  531.     bool b_horz = x_IsHorizontal();
  532.     int pos = m_Border;
  533.     if(m_RootItem) {     
  534.         for( size_t i = 0;  i < m_Entries.size();  i++  )  {
  535.             const CMenuItem* item = m_Entries[i].m_Item;
  536.             int size = m_Entries[i].m_Size;
  537.             
  538.             if(item == &r_item)    { // found r_item
  539.                 if(b_horz)  {
  540.                     return CRect(pos, m_Border, pos + size - 1, m_Height - m_Border - 1);
  541.                 } else {
  542.                     return CRect(m_Border, pos, m_Width - m_Border - 1, pos + size - 1);
  543.                 }
  544.                 break;
  545.             } else {
  546.                 pos += size;
  547.             }
  548.         }
  549.     }
  550.     return CRect(0, 0);
  551. }
  552. // returns item containing (x, y) point
  553. CMenuItem*      CMenu::x_HitTest(int x, int y)
  554. {    
  555.     Fl_Widget& wid = GetWidget();
  556.     bool b_horz = x_IsHorizontal();
  557.     
  558.     // to local coords
  559.     x -= wid.x();
  560.     y -= wid.y();
  561.     int i_x = m_Border;
  562.     int i_y = m_Border;
  563.             
  564.     bool b_hit_rect = (b_horz && y >= m_Border  &&  y < m_Border + m_Height) 
  565.                         || (! b_horz  &&  x >= m_Border  &&  x < m_Border + m_Width);
  566.     if(b_hit_rect  &&  m_RootItem) {
  567.         fl_font(sm_Props.m_FontType, sm_Props.m_FontSize);
  568.         int i = 0;
  569.         for( CMenuItem::TChildItem_I it =  m_RootItem->SubItemsBegin();
  570.                                    it != m_RootItem->SubItemsEnd(); ++it, i++  ) {
  571.             CMenuItem& item = *(*it)->GetValue();
  572.             int size = m_Entries[i].m_Size;
  573.             if(b_horz)  {
  574.                 if(x >= i_x  &&  x < i_x + size)  {
  575.                     return &item;
  576.                 } else  i_x += size;
  577.             } else {
  578.                 if(y >= i_y  &&  y < i_y + size)  {
  579.                     return &item;
  580.                 } else  i_y += size;
  581.             }
  582.         }
  583.     }
  584.     return NULL;
  585. }
  586. void    CMenu::x_ExecuteCommand(const CMenuItem& item)
  587. {
  588.     if(item.IsEnabled()  &&  item.IsItem())  {
  589.         TCmdID cmd = item.GetCommand();        
  590.         if(GetCmdTarget())  {
  591.             GetCmdTarget()->OnCommand(cmd);
  592.         } else {
  593.             ERR_POST("CMenu - cannot execute command, not command target specified");
  594.         }
  595.     }     
  596. }
  597. void    WidgetToScreen(const Fl_Widget& widget, int& wx, int& wy)
  598. {
  599.     for( Fl_Window* w = widget.window(); w; w = w->window())   {
  600.         wx += w->x();
  601.         wy += w->y();
  602.     } 
  603. }
  604. ///////////////////////////////////////////////////////////////////////////////
  605. /// CMenuCmdUI
  606. CMenuCmdUI::CMenuCmdUI(CMenuItem& item)
  607. : m_Item(item)
  608. {
  609. }
  610. TCmdID  CMenuCmdUI::GetCommand() const
  611. {
  612.     return m_Item.GetCommand();
  613. }
  614. void    CMenuCmdUI::Enable(bool en)
  615. {
  616.     m_Item.Enable(en);
  617. }
  618. void    CMenuCmdUI::SetCheck(bool set)
  619. {
  620.     m_Item.SetCheck(set);
  621. }
  622. void    CMenuCmdUI::SetRadio(bool set)
  623. {
  624.     m_Item.SelectRadio(set);
  625. }
  626. void    CMenuCmdUI::SetLabel(const string& label)
  627. {
  628.     m_Item.SetLabel(label);
  629. }
  630. ///////////////////////////////////////////////////////////////////////////////
  631. /// CPopupMenu1
  632. CPopupMenu1::TPopupVector    CPopupMenu1::sm_Popups;
  633. CMenuItem*      CPopupMenu1::sm_CurrItem = NULL;
  634. CPopupMenu1*    CPopupMenu1::sm_CurrPopup = NULL;
  635. CPopupMenu1::EState          CPopupMenu1::sm_State;
  636. bool            CPopupMenu1::sm_bMenubar;
  637. CPopupMenu1::CPopupMenu1(int x, int y, CMenuItem* root_item, 
  638.                          CCommandTarget* target, IMenuHintListener* listener)
  639. :   TPopupMenuParent(x, y, 0, 0)
  640. {
  641.     x_Init(x, y, x, y, root_item, target, listener, true);
  642. }
  643. CPopupMenu1::CPopupMenu1(int x, int y, const SMenuItemRec* items, 
  644.                          CCommandTarget* target, IMenuHintListener* listener)
  645. :   TPopupMenuParent(x, y, 0, 0)
  646. {
  647.     CMenuItem* root_item = CreateMenuItems(items);
  648.     x_Init(x, y, x, y, root_item, target, listener, true);
  649. }
  650. // this is a protected constructor used only within CMenu subsystem
  651. // creates a child popup menu. 
  652. CPopupMenu1::CPopupMenu1(int x, int y, int left, int top, CMenuItem* root_item,
  653.                          CCommandTarget* target, IMenuHintListener* listener)
  654. :   TPopupMenuParent(x, y, 0, 0)
  655. {   
  656.    x_Init(x, y, left, top, root_item, target, listener, false);
  657. }
  658. void    CPopupMenu1::x_Init(int x, int y, int left, int top, CMenuItem* root_item, 
  659.                             CCommandTarget* target, IMenuHintListener* listener, bool root_popup)
  660. {
  661.     m_MenuBar = NULL; // popup mode
  662.     position(x, y);
  663.     m_Left = left;
  664.     m_Top = top;
  665.     
  666.     m_bSkipFirstRelease = root_popup;
  667.     
  668.     m_Cmd = eCmdInvalid;
  669.     m_Border = kBorderSize; 
  670.     m_BackColor = ePopupBack;  // override standard color  
  671.     end(); // prevent adding child widgets
  672.     set_modal();    
  673.     clear_border(); // hide frame
  674.     x_SetItems(root_item, root_popup); // own items only if this is a root popup menu
  675.     SetCmdTarget(target);
  676.     SetHintListener(listener);
  677. }
  678. /// Creates root popup menu stub for a menubar
  679. CPopupMenu1::CPopupMenu1(CMenuBar1& menubar, CMenuItem* root_item, CMenuItem* sel_item,
  680.                          IMenuHintListener* listener)
  681. :   TPopupMenuParent(0, 0, 0, 0),
  682.     m_Left(0), m_Top(0),
  683.     m_MenuBar(&menubar),
  684.     m_bSkipFirstRelease(true),
  685.     m_Cmd(eCmdInvalid)
  686. {
  687.     end(); // prevent adding child widgets
  688.     set_modal();    
  689.     clear_border(); // hide frame
  690.     
  691.     m_Border = m_MenuBar->GetBorder();
  692.     
  693.     m_bKeyActivated = menubar.m_bKeyActivated;
  694.     x_SetItems(root_item, false); // share items with the caller
  695.     SetSelected(sel_item);
  696.     SetHintListener(listener);
  697. }
  698. void    CPopupMenu1::x_AdjustRectangle()
  699. {
  700.     if(m_MenuBar)    {    
  701.         CRect rc = m_MenuBar->GetPopupRect();
  702.         
  703.         const Fl_Widget& wid = m_MenuBar->GetWidget();
  704.         int x = 0, y = 0;
  705.         WidgetToScreen(wid, x,y); // upper-left corner to screen coords
  706.         rc.Offset(x, y);
  707.         resize(rc.Left(), rc.Top(), rc.Width(), rc.Height());
  708.     } else {
  709.         // adjsut size
  710.         TPopupMenuParent::size(m_Width, m_Height);    
  711.         int X = x(), Y = y();
  712.         //adjust position on screen
  713.         if(X + m_Width > Fl::w())  { // not enough space to the right
  714.             if(m_Left == X)   {
  715.                 X = Fl::w() - m_Width;
  716.             } else  {
  717.                 X = m_Left - (m_Width -1); // place to the left
  718.             }
  719.         }
  720.         X = max(X, 0);
  721.         x(X);
  722.     
  723.         if(Y + m_Height > Fl::h())   { // not enought space to the bottom
  724.             if(m_Top == Y)    {
  725.                 Y = Fl::h() - m_Height; // scroll up
  726.             } else  {
  727.                 Y = m_Top - (m_Height - 1); // flip up
  728.             }
  729.         }
  730.         Y = max(Y, 0);
  731.         y(Y);
  732.     }
  733. }
  734. void    CPopupMenu1::SetSelected(CMenuItem* item)
  735. {
  736.     if(m_MenuBar)   { // delegate
  737.         CMenuItem* new_item = NULL;
  738.         if(item)    {
  739.             CMenuItem* root = m_MenuBar->GetRootItem();
  740.             new_item = root->FindEqualSubItem(*item);
  741.             _ASSERT(new_item); // must exist
  742.         }
  743.         m_MenuBar->SetSelected(new_item);
  744.     }
  745.     CMenu::SetSelected(item);
  746. }
  747. CRect   CPopupMenu1::x_GetPopupItemRect(const CMenuItem& item)
  748. {
  749.     CRect rc = CMenu::x_GetPopupItemRect(item);
  750.     if(! x_IsMenuBar()) {
  751.         rc.Inflate(m_Border, 0);
  752.     }
  753.     return rc;
  754. }
  755. /// this functions shows Popup menu and all its submenus and provides event 
  756. /// loop for handling all events.
  757. void    CPopupMenu1::Popup()
  758. {
  759.     if(sm_Popups.size())    {
  760.         ERR_POST("CPopupMenu1::Popup() - must not be called recursively.");
  761.         return;
  762.     }
  763.     sm_Popups.push_back(this);
  764.     sm_bMenubar = (this->m_MenuBar != NULL);
  765.     Fl_Group::current(0);
  766.     //grab events and do initialization
  767.     Fl_Window* prev_grab = Fl::grab();
  768.     Fl::grab(*this);
  769.     // set initial state for this menu
  770.     if(sm_bMenubar) {
  771.         sm_CurrItem = m_Selected; // inherit selection from menubar
  772.         if(m_MenuBar->x_IsKeyActivated())   { // do not expand submenus automatically
  773.             sm_State = eIdle;
  774.         } else {
  775.             m_Selected = NULL; // to trigger submenu expansion
  776.             sm_State = eTrackSubmenus;
  777.         }
  778.     } else {
  779.         sm_CurrItem = NULL;
  780.         sm_State = eIdle;
  781.     }
  782.     sm_CurrPopup = this;
  783.     
  784.     CMenuItem* prev_item = NULL;
  785.     m_Timer.Init(1, kMenuDelay, false, this);
  786.     
  787.     do { // message loop - works while popup menus are on screen
  788.         // show hidden menus
  789.         for( size_t i_popup = 0; i_popup < sm_Popups.size(); i_popup++ ) {
  790.             CPopupMenu1* p_popup = sm_Popups[i_popup];
  791.             if(! p_popup->shown())  {
  792.                 p_popup->show();
  793.             }
  794.         }
  795.         
  796.         Fl::wait(); // handle events and calculate new state
  797.         if(sm_CurrItem != prev_item) {
  798.             x_UpdateListener();
  799.         }
  800.         // performs actions depending on cuurent state
  801.         switch(sm_State)    {
  802.         case eIdle:
  803.         case eDone: break;
  804.         case eHide: { //hide one popup
  805.             if(sm_Popups.size() > 1) { // except the first one                
  806.                 CPopupMenu1* dead_menu = sm_Popups.back();
  807.                 delete dead_menu;
  808.                 sm_Popups.pop_back();
  809.             }
  810.             sm_State = eIdle;
  811.         }; break;
  812.         default:
  813.             if(sm_CurrItem == NULL)    {
  814.                 // turn off selection in deepest menu, but don't erase other menus:
  815.                 if(sm_Popups.size())    {
  816.                     sm_Popups.back()->SetSelected(NULL);
  817.                 }
  818.             } else if(! sm_CurrItem->IsSeparator()) { // change selected itme (if it isn;t a separator)
  819.                 
  820.                 if(sm_State == eTrackSubmenus  &&  sm_CurrPopup->m_Selected == sm_CurrItem)  {
  821.                     sm_State = eIdle; // nothing changed
  822.                 } else  {                                
  823.                     TPopupVector::iterator it = std::find(sm_Popups.begin(), sm_Popups.end(), sm_CurrPopup);
  824.                     CMenuItem* item = sm_CurrItem;                    
  825.                     for( int i = it - sm_Popups.begin(); i >= 0; i-- )    {                        
  826.                         sm_Popups[i]->SetSelected(item);
  827.                         item = item->GetParent();            
  828.                     }
  829.                 }        
  830.         
  831.                 sm_CurrPopup->SetSelected(sm_CurrItem);
  832.                 switch(sm_State)    {
  833.                 case eTrack:    {
  834.                     x_CollapseToCurrent(); // close expanded submenus
  835.                 }; break;
  836.                 case eExpand:   {
  837.                     x_ShowCurrItemSubmenu(true); //expand submenu and select item
  838.                     sm_State = eIdle;
  839.                 }; break;
  840.                 case eMenuPush: 
  841.                 {                                    
  842.                     x_CollapseToCurrent(); // close expanded submenus
  843.                     x_ShowCurrItemSubmenu(false); //expand new submenu
  844.                 }; break;
  845.                 case eTrackSubmenus: {
  846.                     if(sm_CurrPopup  &&  sm_CurrPopup->x_IsMenuBar())   {
  847.                         x_CollapseToCurrent(); // close expanded submenus
  848.                         x_ShowCurrItemSubmenu(false); //expand new submenu
  849.                     } else {
  850.                         m_Timer.ReStart();
  851.                     }
  852.                 }; break;
  853.                 default: break;
  854.                 }; //switch
  855.                 sm_State = eIdle; // procesing finished - go to neutral state
  856.             }; 
  857.             prev_item = sm_CurrItem;            
  858.         }//switch
  859.     } while(sm_State != eDone);
  860.     _ASSERT(sm_State == eDone); // check exit condition
  861.     m_Timer.Stop();
  862.     for( size_t i = 1; i < sm_Popups.size(); i++ )  {
  863.         delete sm_Popups[i];
  864.     }
  865.     sm_Popups.clear();
  866.     
  867.     hide(); // close window
  868.     Fl::grab(prev_grab); //return grab to previous owner
  869.     sm_CurrItem = NULL;
  870.     sm_CurrPopup = NULL;
  871.     sm_State = eIdle;
  872.     if(m_Cmd != eCmdInvalid)   {
  873.         if(GetCmdTarget())  {
  874.             GetCmdTarget()->OnCommand(m_Cmd);
  875.         } else {
  876.             ERR_POST("CMenu cannot execute command - no command target specifed.");
  877.         }
  878.     }
  879. }
  880. // close all submenus up to current menu
  881. void    CPopupMenu1::x_CollapseToCurrent(void)
  882. {
  883.     // delete all submenus from the last one to "sm_CurrPopup"
  884.     while(sm_Popups.size() > 1) { // except the first one
  885.         CPopupMenu1* dead_menu = sm_Popups.back();
  886.         if(dead_menu != sm_CurrPopup)  {
  887.             delete dead_menu;
  888.             sm_Popups.pop_back();
  889.         } else break;
  890.     }
  891. }
  892. void    CPopupMenu1::x_ShowCurrItemSubmenu(bool b_select)
  893. {
  894.     _ASSERT(sm_CurrItem);
  895.     if(sm_CurrItem->IsSubmenu()  &&  ! sm_CurrItem->IsSubmenuEmpty())    { // show submenu
  896.         CRect rc = sm_CurrPopup->x_GetPopupItemRect(*sm_CurrItem);
  897.         rc.Offset(sm_CurrPopup->x(), sm_CurrPopup->y()); // to screen coords
  898.         int x, y;
  899.         if(sm_CurrPopup->x_IsMenuBar())   {
  900.             x = rc.Left();
  901.             y = rc.Bottom();
  902.         } else {
  903.             x = rc.Right();
  904.             y = rc.Top();
  905.         }
  906.         int left = rc.Left();
  907.         int top = rc.Top();
  908.         CPopupMenu1* submenu = new CPopupMenu1(x, y, left, top, sm_CurrItem, 
  909.                                                GetCmdTarget(), GetHintListener());
  910.         sm_Popups.push_back(submenu);        
  911.         
  912.         sm_CurrPopup = submenu;
  913.         if(b_select)    {
  914.             x_SelectNextItem();
  915.         } else {
  916.             sm_CurrItem = NULL;            
  917.         }
  918.         sm_CurrPopup->SetSelected(sm_CurrItem);                        
  919.         sm_CurrPopup->show();
  920.     }
  921. }
  922. // changes current item's state to opposite
  923. void    CPopupMenu1::x_TogglePushItem()
  924. {
  925.     _ASSERT(m_bKeyActivated); // not supposed to be used overwise
  926.     
  927.     if(m_PushedItem == NULL)    {
  928.         m_MenuBar->m_PushedItem = m_PushedItem = sm_CurrItem;        
  929.     } else {
  930.         m_MenuBar->m_PushedItem = m_PushedItem = NULL;
  931.         redraw();
  932.         x_CloseItemSubmenu(); // collapse menu hierarhy        
  933.     }
  934.     redraw();
  935. }
  936. /// handles events for all CPopupMenu-s being active at the moment
  937. int     CPopupMenu1::handle(int event)
  938. {   
  939.     m_Event.OnFLTKEvent(event);
  940.     switch(event)   {
  941.     case FL_KEYDOWN: return x_HandleKeyboard();
  942.     case FL_ENTER:
  943.     case FL_LEAVE:
  944.     case FL_MOVE:
  945.     case FL_DRAG:
  946.     case FL_PUSH: 
  947.     case FL_RELEASE:   {
  948.         // locate menu item being hit
  949.         CPopupMenu1* popup = NULL;
  950.         CMenuItem* item = NULL;
  951.         int i_popup = sm_Popups.size() - 1;
  952.         while(item == NULL  &&  i_popup >= 0) {
  953.             popup = sm_Popups[i_popup];
  954.             int x = Fl::event_x_root();
  955.             int y = Fl::event_y_root();
  956.             item = popup->x_HitTest(x, y);
  957.             if(item == NULL)  {
  958.                 i_popup--;
  959.             }
  960.         }        
  961.         if(item  &&  item->IsSeparator())  {
  962.             item = NULL;
  963.         }
  964.         
  965.         switch(event)   {
  966.         case FL_PUSH:    {  
  967.             sm_CurrItem = item;
  968.             sm_CurrPopup = item ? popup : NULL;
  969.             // if m_bSkipFirstRelease is "true" then Popup was shown on FL_RELEASE
  970.             // and we do not need to skip next FL_RELEASE
  971.             m_bSkipFirstRelease = false; 
  972.             
  973.             if(sm_CurrItem  &&  sm_CurrItem->IsSubmenu()) {
  974.                 if(x_IsKeyActivated())   {
  975.                     x_TogglePushItem();
  976.                     sm_State = m_PushedItem ? eMenuPush : eHide;
  977.                     return 1;
  978.                 }
  979.                 sm_State = eMenuPush;
  980.             } else  {
  981.                 sm_State = ePush;
  982.             }
  983.         }; break;
  984.         case FL_RELEASE:    {            
  985.             if(m_bSkipFirstRelease)   {
  986.                 m_bSkipFirstRelease = false;
  987.             } else {
  988.                 bool b_skip  = x_IsKeyActivated()  ||  (item  &&  ! item->IsEnabled());
  989.                 if(! b_skip)    {
  990.                     if(item == NULL  ||  item->IsItem()) // command item pushed
  991.                     {
  992.                         if(item)    {
  993.                             x_SetExecuteCommand(*item);
  994.                         }
  995.                         sm_State = eDone;
  996.                     } else if(popup  &&  popup->m_MenuBar)  {
  997.                         sm_State = eDone;
  998.                     }
  999.                 }
  1000.             }   
  1001.         }; break;
  1002.         default: {
  1003.             if(! x_IsKeyActivated()  || item)   {
  1004.                 sm_CurrItem = item;
  1005.                 sm_CurrPopup = item ? popup : NULL;             
  1006.             }
  1007.             sm_State = (! x_IsKeyActivated()  ||  m_PushedItem) ? eTrackSubmenus : eTrack;
  1008.         };
  1009.         };
  1010.         if(event == FL_ENTER ||  event == FL_LEAVE) {
  1011.             redraw(); // we need to repaint itseld to reflect changes in "focused" state
  1012.         }
  1013.         return 1;
  1014.     };
  1015.     };// switch
  1016.     return Fl_Window::handle(event);
  1017. }
  1018. int     CPopupMenu1::x_HandleKeyboard()
  1019. {
  1020.     if(x_HandleShortcuts())  {
  1021.         return 1;
  1022.     }
  1023.     int key = Fl::event_key();
  1024.     if(key == FL_Tab)   {
  1025.         key = (Fl::event_shift()) ? FL_BackSpace : FL_Down;
  1026.     } 
  1027.     
  1028.     switch(key) {
  1029.     case FL_Down: {
  1030.         if(! sm_CurrPopup->x_IsMenuBar())    { // Up-Down works only in submenus
  1031.             x_SelectNextItem();
  1032.         } else if(x_IsKeyActivated())    {
  1033.             x_TogglePushItem();
  1034.             sm_State = eExpand;
  1035.         }
  1036.         return 1;
  1037.     };
  1038.     case FL_Up: {
  1039.         if(! sm_CurrPopup->x_IsMenuBar())    { // Up-Down works only in submenus
  1040.             x_SelectPrevItem();
  1041.         }
  1042.         return 1;
  1043.     };
  1044.     case FL_Right: {
  1045.         x_OnRightPressed();
  1046.         return 1;
  1047.     }; 
  1048.     case FL_Left: {
  1049.         x_OnLeftPressed(); 
  1050.         return 1;
  1051.     };
  1052.     case FL_Page_Up:
  1053.     case FL_Home:   {
  1054.         x_SelectTopItem();
  1055.         return 1;
  1056.     }; 
  1057.     case FL_Page_Down:
  1058.     case FL_End:    {
  1059.         x_SelectBottomItem();
  1060.         return 1;
  1061.     };
  1062.     case FL_Enter:
  1063.     case ' ':   {
  1064.         if(sm_CurrItem  &&  sm_CurrItem->IsEnabled())   {
  1065.             if(sm_CurrItem->IsItem())  {
  1066.                 x_SetExecuteCommand(*sm_CurrItem);
  1067.                 sm_State = eDone;
  1068.             } else {
  1069.                 sm_State = eExpand;
  1070.             }
  1071.         }         
  1072.         return 1;
  1073.     };
  1074.     case FL_Escape: {
  1075.         if(x_IsMenuBar()  &&  x_IsKeyActivated()  &&  sm_Popups.size() > 1) {
  1076.             x_TogglePushItem(); // "unpush" selected item and close submenus
  1077.         } else {
  1078.             sm_CurrPopup = NULL; 
  1079.             sm_State = eDone; // exit menus
  1080.         }
  1081.         return 1;
  1082.     };
  1083.     };
  1084.     return 0;
  1085. }
  1086. // handle both type of keyboard shortcuts - Access Keys and Accelerators
  1087. int    CPopupMenu1::x_HandleShortcuts(void)
  1088. {
  1089.     // first try accelerators
  1090.     int accel = m_Event.GetAccelByEvent();
  1091.     TCmdID cmd = eCmdInvalid;
  1092.     if(CAccelTable::GetCommandByAccel(accel, cmd))    {
  1093.         m_Cmd = cmd;
  1094.         sm_State = eDone;
  1095.         return 1;
  1096.     }
  1097.     // now handling access keys
  1098.     if(sm_CurrPopup)    {
  1099.         char ch = tolower(Fl::event_key());
  1100.         TAccessKeyToItemMap::const_iterator it = sm_CurrPopup->m_AccessKeyToItem.find(ch);
  1101.     
  1102.         if(it != sm_CurrPopup->m_AccessKeyToItem.end())   {
  1103.             sm_CurrItem = it->second;
  1104.             if(sm_CurrItem  &&  sm_CurrItem->IsEnabled())   {
  1105.                 if(sm_CurrItem->IsItem())  {
  1106.                     x_SetExecuteCommand(*sm_CurrItem);
  1107.                     sm_State = eDone;
  1108.                 } else if(sm_CurrItem->IsSubmenu())    {
  1109.                     sm_State = sm_CurrPopup->x_IsMenuBar() ? eTrackSubmenus : eTrack;
  1110.                 }
  1111.             }         
  1112.             return 1;
  1113.         }
  1114.     }
  1115.     return 0;
  1116. }
  1117. void    CPopupMenu1::x_OnLeftPressed(void)
  1118. {
  1119.     if(x_IsMenuBar())   { // root menu is MenuBar
  1120.         if(sm_Popups.size() > 2)    { // close last menu
  1121.             x_CloseItemSubmenu();            
  1122.         } else  { // select previos item in MenuBar
  1123.             sm_CurrPopup = this;
  1124.             x_SelectPrevItem();
  1125.         }
  1126.     } else { // root menu is Popup
  1127.         x_CloseItemSubmenu();
  1128.     }
  1129. }
  1130. void    CPopupMenu1::x_OnRightPressed(void)
  1131. {
  1132.     if(sm_CurrPopup->m_Selected  &&  ! sm_CurrPopup->m_Selected->IsSubmenuEmpty()  &&  m_PushedItem) {
  1133.         sm_State = eExpand;
  1134.     } else {         
  1135.         if(x_IsMenuBar()) {//  &&  sm_Popups.size() > 1  &&  sm_CurrPopup == sm_Popups[1])  {
  1136.             // if current popup is MenuBar's submenu
  1137.             sm_CurrPopup = this; // MenuBar
  1138.             x_SelectNextItem();  // select next item (to the right) in MenuBar
  1139.         }
  1140.     }
  1141. }
  1142. // selects next item in the current menu (Popup or MenuBar)
  1143. void    CPopupMenu1::x_SelectNextItem(void)
  1144. {
  1145.     _ASSERT(sm_CurrPopup);
  1146.     CMenuItem& root = *sm_CurrPopup->m_RootItem;
  1147.     if(! root.IsSubmenuEmpty())    {
  1148.         // find selected ot first if nothing is selected
  1149.         CMenuItem::TChildItem_I it = sm_CurrPopup->m_Selected ? 
  1150.             root.FindSubItem(*sm_CurrPopup->m_Selected) : --root.SubItemsEnd();
  1151.         
  1152.         _ASSERT(it != root.SubItemsEnd());
  1153.         
  1154.         CMenuItem* item = NULL;
  1155.         do {
  1156.             ++it;
  1157.             if(it == root.SubItemsEnd())    {
  1158.                 it = root.SubItemsBegin(); 
  1159.             }
  1160.             item = (*it)->GetValue();
  1161.         } while(item->IsSeparator()  &&  item != sm_CurrPopup->m_Selected);
  1162.     
  1163.         sm_CurrItem = item;
  1164.         sm_State = (sm_CurrPopup->x_IsMenuBar()  &&  ! sm_CurrItem->IsSubmenuEmpty()  &&  m_PushedItem) ? eTrackSubmenus : eTrack;
  1165.     }
  1166. }
  1167. // selects next item in the current menu (Popup or MenuBar)
  1168. void    CPopupMenu1::x_SelectPrevItem(void)
  1169. {
  1170.     _ASSERT(sm_CurrPopup);
  1171.     CMenuItem& root = *sm_CurrPopup->m_RootItem;
  1172.     if(! root.IsSubmenuEmpty())    {
  1173.         // find selected ot fisrt if nothing is selected
  1174.         CMenuItem::TChildItem_I it = sm_CurrPopup->m_Selected ? 
  1175.             root.FindSubItem(*sm_CurrPopup->m_Selected) : root.SubItemsBegin();
  1176.         
  1177.         _ASSERT(it != root.SubItemsEnd());
  1178.     
  1179.         CMenuItem* item = NULL;
  1180.         do {
  1181.             if(it == root.SubItemsBegin())    {
  1182.                 it = root.SubItemsEnd(); 
  1183.             }
  1184.             --it;
  1185.             item = (*it)->GetValue();
  1186.         } while(item->IsSeparator()  &&  item != sm_CurrPopup->m_Selected);
  1187.     
  1188.         sm_CurrItem = item;
  1189.         //sm_State = sm_CurrPopup->x_IsMenuBar() ? eTrackSubmenus : eTrack;
  1190.         sm_State = (sm_CurrPopup->x_IsMenuBar()  &&  ! sm_CurrItem->IsSubmenuEmpty()  &&  m_PushedItem) ? eTrackSubmenus : eTrack;
  1191.     }
  1192. }
  1193. void    CPopupMenu1::x_SelectTopItem(void)
  1194. {
  1195.     if(! sm_CurrPopup->x_IsMenuBar()) {
  1196.         for(size_t i = 0; i < sm_CurrPopup->m_Entries.size(); i++ )  {
  1197.             SItemEntry& entry = sm_CurrPopup->m_Entries[i];
  1198.             CMenuItem* item = entry.m_Item;
  1199.             if(! item->IsSeparator()  &&  item->IsEnabled())    {
  1200.                 sm_CurrItem = item;
  1201.                 sm_State = eTrack; // select
  1202.                 return;
  1203.             }
  1204.         }
  1205.     }
  1206. }
  1207. void    CPopupMenu1::x_SelectBottomItem(void)
  1208. {
  1209.     if(! sm_CurrPopup->x_IsMenuBar()) {
  1210.         for(int i = sm_CurrPopup->m_Entries.size() - 1; i >= 0;  i-- )  {
  1211.             SItemEntry& entry = sm_CurrPopup->m_Entries[i];
  1212.             CMenuItem* item = entry.m_Item;
  1213.             if(! item->IsSeparator()  &&  item->IsEnabled())    {
  1214.                 sm_CurrItem = item;
  1215.                 sm_State = eTrack; // select
  1216.                 return;
  1217.             }
  1218.         }
  1219.     }
  1220. }
  1221. void    CPopupMenu1::x_CloseItemSubmenu(void)
  1222. {
  1223.     if(sm_Popups.size() > 1) {
  1224.         CPopupMenu1* menu = sm_Popups.back();
  1225.         if(sm_CurrPopup == menu)    { // select previous one
  1226.             sm_CurrPopup = sm_Popups[sm_Popups.size() - 2];
  1227.             sm_CurrItem = sm_CurrPopup->m_Selected;
  1228.         }
  1229.         sm_State = eHide;
  1230.     }
  1231. }
  1232. ///  sets command that will be executed on exit
  1233. void    CPopupMenu1::x_SetExecuteCommand(const CMenuItem& item)
  1234. {
  1235.     if(item.IsEnabled()  &&  item.IsItem())  {
  1236.         m_Cmd = item.GetCommand();        
  1237.     } else {
  1238.         m_Cmd = eCmdInvalid;
  1239.     }     
  1240. }
  1241. /// depending on selected item generates text string and supplies it to the 
  1242. /// listener
  1243. void    CPopupMenu1::x_UpdateListener()
  1244. {
  1245.     if(m_Listener)    {
  1246.         string hint;
  1247.         if(sm_CurrItem  &&  sm_CurrItem->IsItem())    {
  1248.             hint = GetCmdHint(sm_CurrItem->GetCommand());
  1249.         }
  1250.         m_Listener->OnHint(hint);
  1251.     }
  1252. }
  1253. void    CPopupMenu1::show(void)
  1254.     x_AdjustRectangle();
  1255.     x_UpdateItems();
  1256.     
  1257.     TPopupMenuParent::show();
  1258. }
  1259. void    CPopupMenu1::draw(void)
  1260. {    
  1261.     if(m_MenuBar)   { // delegate draing to hosting MenuBar
  1262.         m_MenuBar->x_Draw(0, 0);
  1263.     } else {
  1264.         CMenu::x_Draw(0, 0);
  1265.     }
  1266. }
  1267. Fl_Widget&  CPopupMenu1::GetWidget(void)
  1268. {
  1269.     return *static_cast<Fl_Widget*>(this);
  1270. }
  1271. const Fl_Widget&  CPopupMenu1::GetWidget(void) const
  1272. {
  1273.     return *static_cast<const Fl_Widget*>(this);
  1274. }
  1275. bool    CPopupMenu1::x_IsHorizontal(void) const
  1276. {
  1277.     return m_MenuBar != NULL;
  1278. }
  1279. void    CPopupMenu1::OnTimeout(int timer_id)
  1280. {
  1281.     m_Timer.Stop();
  1282.     if(timer_id == 1)   {        
  1283.         if(sm_CurrPopup  && sm_CurrItem)    {
  1284.             x_CollapseToCurrent(); // close expanded submenus
  1285.             x_ShowCurrItemSubmenu(false); //expand new submenu
  1286.         }
  1287.     }
  1288. }
  1289. ///////////////////////////////////////////////////////////////////////////////
  1290. /// CMenuBar1
  1291. CMenuBar1::CMenuBar1(int x, int y, int w, int h)
  1292. :   Fl_Widget(x, y, w, h),
  1293.     m_TooltipItem(NULL)
  1294. {
  1295.     m_Border = 2;
  1296.     
  1297.     m_Tooltip.SetMode(CTooltip::eStayOnMove);
  1298.     m_Tooltip.EnableActiveMode(this);
  1299. }
  1300. void    CMenuBar1::EnableTooltips(bool b_en)
  1301. {
  1302.     m_Tooltip.Enable(b_en);
  1303. }
  1304. void    CMenuBar1::Activate()
  1305. {
  1306.     if(m_RootItem  &&  ! m_RootItem->IsSubmenuEmpty())  {
  1307.         CMenuItem* item = (* m_RootItem->SubItemsBegin())->GetValue();
  1308.         
  1309.         SetSelected(item);
  1310.         m_PushedItem = NULL; // not pushed by default
  1311.         m_bKeyActivated = true;
  1312.         
  1313.         x_ShowSubMenu();
  1314.         
  1315.         m_bKeyActivated = false;
  1316.     }
  1317. }
  1318. void    CMenuBar1::draw(void)
  1319. {
  1320.    Fl_Widget& wid = GetWidget();
  1321.    x_Draw(wid.x(), wid.y());
  1322. }
  1323. void    CMenuBar1::x_Draw(int x, int y)
  1324. {    
  1325.    CMenu::x_Draw(x, y); 
  1326.    
  1327.    Fl_Widget& wid = GetWidget();
  1328.    fl_draw_box(FL_THIN_UP_FRAME, x, y, wid.w(), wid.h(), sm_Props.GetColor(eBack));
  1329. }
  1330. Fl_Widget&  CMenuBar1::GetWidget(void)
  1331. {
  1332.     return *static_cast<Fl_Widget*>(this);
  1333. }
  1334. const Fl_Widget&  CMenuBar1::GetWidget() const
  1335. {
  1336.     return *static_cast<const Fl_Widget*>(this);
  1337. }
  1338. CRect   CMenuBar1::GetPopupRect(void) const
  1339. {
  1340.     int w = GetWidget().w();
  1341.     int h = GetWidget().h();
  1342.     return CRect(x(), y(), x() + w -1, y() + h - 1);
  1343. }
  1344. int     CMenuBar1::handle(int event)
  1345. {
  1346.     int res = x_Handle(event);
  1347.     return res ? res : Fl_Widget::handle(event);
  1348. }
  1349. int     CMenuBar1::x_Handle(int event)
  1350. {
  1351.     m_Tooltip.Handle(event);
  1352.     switch(event)   {
  1353.     case FL_ENTER:
  1354.     case FL_MOVE:
  1355.     case FL_DRAG:
  1356.     case FL_PUSH:   
  1357.     case FL_LEAVE: {
  1358.         CMenuItem* item = x_HitTest(Fl::event_x(), Fl::event_y());
  1359.         if(item  && item->IsSeparator())  {
  1360.             item = NULL;
  1361.         }
  1362.         SetSelected(item);
  1363.         
  1364.         switch(event)   {
  1365.         case FL_PUSH:    {
  1366.             m_PushedItem = item;
  1367.             if(m_Selected  &&  m_Selected->IsEnabledSubmenu())    {            
  1368.                 // FL_RELEASE for this FL_PUSH will be handled inside popup menu stub
  1369.                 x_ShowSubMenu();                    
  1370.                 // when we return from x_ShowSubMenu() it should not be considered pressed
  1371.                 m_PushedItem = NULL;
  1372.             }
  1373.             GetWidget().redraw();            
  1374.         }; break;
  1375.         case FL_ENTER:
  1376.         case FL_LEAVE:  {
  1377.             redraw();
  1378.         }; break;
  1379.         };
  1380.         return 1;
  1381.     };
  1382.     case FL_RELEASE:    {            
  1383.         CMenuItem* item = x_HitTest(Fl::event_x(), Fl::event_y());
  1384.         if(item  &&  item == m_PushedItem  //&&  Fl::event_is_click()
  1385.             &&  x_IsItemPushed())    {
  1386.             x_ExecuteCommand(*m_PushedItem); 
  1387.         }
  1388.         m_PushedItem = NULL;
  1389.         GetWidget().redraw();
  1390.         return 1;
  1391.     }
  1392.     };
  1393.     return 0;    
  1394. }
  1395. int     CMenuBar1::x_HandleShortcuts(void)
  1396. {
  1397.     char ch = tolower(Fl::event_key());
  1398.     TAccessKeyToItemMap::const_iterator it = m_AccessKeyToItem.find(ch);
  1399.     
  1400.     if(it != m_AccessKeyToItem.end())   {
  1401.         SetSelected(it->second);
  1402.         x_ShowSubMenu();
  1403.         return 1;
  1404.     }
  1405.     return 0;
  1406. }
  1407. void    CMenuBar1::x_ShowSubMenu(void)
  1408. {
  1409.     if(m_Selected->IsEnabled()) {
  1410.         if(m_Selected->IsSubmenu()  &&  ! m_Selected->IsSubmenuEmpty()) {   // show popup menu                  
  1411.             CPopupMenu1 popup(*this, m_RootItem, m_Selected, GetHintListener());
  1412.             popup.SetCmdTarget(GetCmdTarget());
  1413.             popup.Popup();
  1414.             SetSelected(NULL);
  1415.         }
  1416.     }    
  1417. }
  1418. bool    CMenuBar1::x_IsHorizontal(void) const
  1419. {
  1420.     return true;
  1421. }
  1422. bool    CMenuBar1::TC_NeedTooltip(int x, int y)
  1423. {
  1424.     m_TooltipItem = x_HitTest(x, y);
  1425.     bool en = m_TooltipItem  &&  ! m_TooltipItem->IsSeparator();
  1426.     return en;
  1427. }
  1428. string  CMenuBar1::TC_GetTooltip(int& x, int& y, int& w, int& h)
  1429. {
  1430.     _ASSERT(m_TooltipItem);
  1431.     CRect rc = x_GetPopupItemRect(*m_TooltipItem);
  1432.     x = rc.Left();
  1433.     y = rc.Top();
  1434.     w = rc.Width();
  1435.     h = rc.Height();
  1436.     return x_GetTooltip(*m_TooltipItem);
  1437. }
  1438. string  CMenuBar1::x_GetTooltip(const CMenuItem& item) const
  1439. {
  1440.     // find accelerator label if any
  1441.     string s_accel;
  1442.     ITERATE(TEntries, it, m_Entries)  {
  1443.         if(it->m_Item == &item) {
  1444.             s_accel = it->m_AccelLabel;
  1445.             break;
  1446.         }
  1447.     }
  1448.     string label = item.GetLabel();
  1449.     string::size_type pos = label.find(kAccessKeyMarker); // find accelerator marker
  1450.     if(pos != string::npos  &&  pos + 1 < label.size())   { // there is an accelerator        
  1451.         label.erase(pos, 1);
  1452.     }
  1453.     
  1454.     if(s_accel.size())  {
  1455.         label += " (";
  1456.         label += s_accel + ")";
  1457.     }
  1458.     return label;
  1459. }
  1460. END_NCBI_SCOPE
  1461. /*
  1462.  * ===========================================================================
  1463.  * $Log: menu_window.cpp,v $
  1464.  * Revision 1000.0  2004/06/01 21:29:21  gouriano
  1465.  * PRODUCTION: IMPORTED [GCC34_MSVC7] Dev-tree R1.7
  1466.  *
  1467.  * Revision 1.7  2004/05/21 22:27:53  gorelenk
  1468.  * Added PCH ncbi_pch.hpp
  1469.  *
  1470.  * Revision 1.6  2004/05/13 17:29:39  yazhuk
  1471.  * Added support for accelerators, keyboard activated menus
  1472.  *
  1473.  * Revision 1.5  2004/05/10 20:58:11  ucko
  1474.  * +<math.h> for ceil
  1475.  *
  1476.  * Revision 1.4  2004/05/10 16:25:40  yazhuk
  1477.  * Addressed GCC warnings
  1478.  *
  1479.  * Revision 1.3  2004/05/07 14:19:29  yazhuk
  1480.  * Added UpdateItems(), fixed focus handling
  1481.  *
  1482.  * Revision 1.2  2004/05/03 19:47:36  yazhuk
  1483.  * Refactoring; support for command updates
  1484.  *
  1485.  * Revision 1.1  2004/04/22 16:56:35  yazhuk
  1486.  * Initial revision
  1487.  *
  1488.  * ===========================================================================
  1489.  */