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

生物技术

开发平台:

C/C++

  1. /*
  2.  * ===========================================================================
  3.  * PRODUCTION $Log: combobox.cpp,v $
  4.  * PRODUCTION Revision 1000.3  2004/06/01 21:08:58  gouriano
  5.  * PRODUCTION PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.16
  6.  * PRODUCTION
  7.  * ===========================================================================
  8.  */
  9. /*  $Id: combobox.cpp,v 1000.3 2004/06/01 21:08:58 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.  * Author: Philip Johnson
  35.  *
  36.  * File Description: combobox.cpp -- dropdown combobox widget for FLTK
  37.  *
  38.  */
  39. #include <ncbi_pch.hpp>
  40. #include <corelib/ncbistl.hpp>
  41. #include <gui/widgets/fl/combobox.hpp>
  42. #include <FL/Fl_Box.H>
  43. #include <FL/Fl_Button.H>
  44. #include <FL/Fl_Input.H>
  45. BEGIN_NCBI_SCOPE
  46. //-----------------------------------------------------------------------------
  47. // PRE : undo information stored in member variables
  48. // POST: the previous value, position & mark have been restored
  49. void CComboBox::CInput::Undo(void)
  50. {
  51.     value(m_Value.c_str());
  52.     position(m_Position, m_Mark);
  53. }
  54. //-----------------------------------------------------------------------------
  55. // PRE : input event
  56. // POST: if ctrl pressed, parent window is checked for shortcuts; current
  57. // input information (value, mark, position) is stored for undo purposes
  58. int CComboBox::CInput::handle(int e)
  59. {
  60.     if (e == FL_KEYBOARD) {
  61.         if ((Fl::event_state() & FL_CTRL)  &&
  62.             window()->handle(FL_SHORTCUT)) { //check for shortcuts
  63.             return 1;
  64.         }
  65.         m_Value = value();
  66.         m_Mark = mark();
  67.         m_Position = position();
  68.     }
  69.     return Fl_Input::handle(e);
  70. }
  71. //-----------------------------------------------------------------------------
  72. // PRE : browser event
  73. // POST: standard browser event handler; if value() is 0 & down arrow
  74. // pressed, value changed to 1 (bug in std handler changes the value to 2)
  75. int CComboBox::CBrowser::handle(int e)
  76. {
  77.     if (e == FL_KEYBOARD  &&  Fl::event_key() == FL_Down  &&  value() == 0) {
  78.         value(1);
  79.         do_callback();
  80.         return 1;
  81.     }
  82.     return Fl_Hold_Browser::handle(e);
  83. }
  84. //-----------------------------------------------------------------------------
  85. // PRE : a widget
  86. // POST: whether the current event *ROOT* is found within the box specified
  87. // by the widget
  88. bool EventRootInside(Fl_Widget *w) {
  89.     if (!w) {
  90.         return false;
  91.     }
  92.     return !(Fl::event_x_root() < w->x()  ||
  93.              Fl::event_x_root() >= w->x() + w->w()  ||
  94.              Fl::event_y_root() < w->y()  ||
  95.              Fl::event_y_root() >= w->y() + w->h());
  96. }
  97. //-----------------------------------------------------------------------------
  98. // PRE : standard fltk boundaries
  99. // POST: popupwin created
  100. CComboBox::CPopupWin::CPopupWin(int x, int y, int w, int h)
  101.     : Fl_Menu_Window(x,y,w,h), m_MouseOver(NULL)
  102. {
  103.     clear_border();
  104.     size_range(1,1);
  105. }
  106. //-----------------------------------------------------------------------------
  107. // PRE : a grabbed event 
  108. // POST: event passed to leaf widgets as appropriate
  109. int CComboBox::CPopupWin::handle(int event)
  110. {
  111.     switch (event) {
  112.     case FL_SHOW:
  113.         m_MouseOver = NULL;
  114.         break;
  115.     case FL_MOVE:
  116.         {   
  117.             // We moved outside the top-level window; close it!
  118.             Fl_Window *top = Fl::first_window();
  119.             if (top == this) {
  120.                 top = Fl::next_window(top);
  121.             }
  122.             if (!EventRootInside(top)  &&
  123.                 !EventRootInside(this)) {
  124.                 do_callback();
  125.                 return 1;
  126.             }
  127.         }
  128.         {   
  129.             // Identify & process leave events
  130.             int childI;
  131.             for (childI = children() - 1;
  132.                  childI >= 0  &&  !Fl::event_inside(child(childI));
  133.                  --childI);
  134.             
  135.             if (childI < 0) {
  136.                 if (m_MouseOver) {
  137.                     m_MouseOver->handle(FL_LEAVE);
  138.                     m_MouseOver = NULL;
  139.                 }
  140.             } else if (child(childI) != m_MouseOver) {
  141.                 if (m_MouseOver) {
  142.                     m_MouseOver->handle(FL_LEAVE);
  143.                 }
  144.                 m_MouseOver = child(childI);
  145.             }
  146.         }
  147.         break;
  148.     case FL_PUSH:
  149.         if (Fl::event_x() < 0  ||  Fl::event_x() >= w()  ||
  150.             Fl::event_y() < 0  ||  Fl::event_y() >= h()) {
  151.             do_callback();
  152.             return 1;
  153.         }
  154.         break;
  155.     case FL_KEYBOARD:
  156.     case FL_SHORTCUT:
  157.         switch(Fl::event_key()) {
  158.         case FL_Tab:
  159.         case FL_Enter:
  160.         case FL_Escape:
  161.         case FL_Control_L:
  162.         case FL_Alt_L:
  163.         case FL_Alt_R:
  164.             do_callback();
  165.             return 0;
  166.         default: break;
  167.         }
  168.         for (int childI = children()-1;  childI >= 0;  --childI) {
  169.             if (child(childI)->handle(event) != 0) {
  170.                 return 1;
  171.             }
  172.         }
  173.         break;
  174.     default: break;
  175.     }
  176.     return Fl_Menu_Window::handle(event);
  177. }
  178. //-----------------------------------------------------------------------------
  179. // PRE :
  180. // POST:
  181. CComboBox::CComboBox(int x, int y, int w, int h, const char* label)
  182.     : Fl_Group(x,y,w,h+1, label), m_InKeypressed(false), m_EntryMap(new CMap)
  183. {
  184.     when(FL_WHEN_CHANGED);
  185.         
  186.     int dx = Fl::box_dx(FL_DOWN_FRAME);
  187.     int dy = Fl::box_dy(FL_DOWN_FRAME);
  188.     int buttonD = h-2*dy;
  189.     {
  190.         m_Box = new Fl_Box(FL_DOWN_FRAME, x,y,w,h, NULL);
  191.         m_Input = new CInput(x+dx, y+dy, w-2*dx-buttonD-1, buttonD);
  192.         m_Input->box(FL_FLAT_BOX);
  193.         m_Input->when(FL_WHEN_CHANGED);
  194.         m_Input->callback((Fl_Callback*) s_InputCB, this);
  195.         
  196.         m_Button = new Fl_Button(x+(w-dx-buttonD), y+dy, buttonD,buttonD);
  197.         m_Button->clear_visible_focus();
  198.         m_Button->label("@-12>");
  199.         m_Button->callback((Fl_Callback*) s_ButtonCB, this);
  200.     }
  201.     // Hidden window, mimics group
  202.     Fl_Group::current(0);
  203.     m_PopupWin = new CPopupWin(x, y, w, h*4);
  204.     {
  205.         m_Browser = new CBrowser(0, h, w, h*3);
  206.         m_Browser->box(FL_BORDER_BOX);
  207.         m_Browser->callback((Fl_Callback*) s_BrowserCB, this);
  208.     }
  209.     begin();
  210.     m_PopupWin->resizable(m_PopupWin);
  211.     m_PopupWin->callback((Fl_Callback*) s_CloseCB, this);
  212.     m_PopupWin->end();
  213.     m_PopupWin->hide();
  214.     m_ResizeBox = new Fl_Box(m_Input->x(), m_Input->y(), m_Input->w(), 1);
  215.     m_ResizeBox->hide();
  216.     resizable(m_ResizeBox);
  217.     end();
  218. }
  219. //-----------------------------------------------------------------------------
  220. // PRE : event for whole combobox
  221. // POST: open browser on down arrow; close browser on escape
  222. int CComboBox::handle(int event)
  223. {
  224.     switch (event) {
  225.     case FL_KEYBOARD:
  226.         switch (Fl::event_key()) {
  227.         case FL_Down:
  228.         case FL_Up:
  229.             {
  230.                 int curr = value_index();
  231.                 if (curr == -1  ||
  232.                     !((m_Input->mark() == 0 &&
  233.                        m_Input->position() == m_Input->size()) ||
  234.                       (m_Input->position() == 0 &&
  235.                        m_Input->mark() == m_Input->size()))) {
  236.                     x_OpenBrowser();
  237.                 } else {
  238.                     int next = (Fl::event_key() == FL_Down) ? curr+1 : curr-1;
  239.                     if (next >= 0  &&  next < (int) m_Entries.size()) {
  240.                         value(m_Entries[next].c_str());
  241.                     }
  242.                 }
  243.                 return 1;
  244.             }
  245.         case FL_Enter:
  246.             x_MaybeCallback();
  247.         default:
  248.             break;
  249.         }
  250.         break;
  251.     case FL_UNFOCUS:
  252.         x_MaybeCallback();
  253.         break;
  254.     default:
  255.         break;
  256.     }
  257.     return Fl_Group::handle(event);
  258. };
  259. //-----------------------------------------------------------------------------
  260. // PRE : new value for combobox
  261. // POST: input box set to that value; browser scrolled to it as, if possible
  262. void CComboBox::value(const char* s)
  263. {
  264.     m_Input->value(s);
  265.     int idx = value_index();
  266.     if (idx != -1) {
  267.         m_Browser->value(idx+1);
  268.     }
  269. }
  270. //-----------------------------------------------------------------------------
  271. // PRE : none
  272. // POST: the current value of the combobox
  273. const char* CComboBox::value(void) const
  274. {
  275.     return m_Input->value();
  276. }
  277. //-----------------------------------------------------------------------------
  278. // PRE : none
  279. // POST: the index of the current value in the entry list (or -1 if not found)
  280. int CComboBox::value_index(void) const
  281. {
  282.     CMap::const_iterator i = 
  283.         m_EntryMap->FindExact(m_Input->value(), (type() & eCaseSensitive) > 0);
  284.     if (i == m_EntryMap->end()) {
  285.         return -1;
  286.     } else {
  287.         return i->second;
  288.     }
  289. }
  290. //-----------------------------------------------------------------------------
  291. // PRE : conditions ripe for potential callback
  292. // POST: callback executed, depending on WHEN mask
  293. void CComboBox::x_MaybeCallback(void)
  294. {
  295.     if ((when() & FL_WHEN_NOT_CHANGED)  ||
  296.         ((when() & FL_WHEN_CHANGED)  &&
  297.          m_PrevValue != m_Input->value())) {
  298.         if (when() & FL_WHEN_RELEASE_ALWAYS) {
  299.             do_callback();
  300.         } else if (value_index() != -1) {
  301.             do_callback();
  302.         }
  303.     }
  304.     m_PrevValue = m_Input->value();
  305. }
  306. //-----------------------------------------------------------------------------
  307. // PRE : down-arrow button clicked
  308. // POST: if browser was open, it's now closed; if it was closed, it's now open
  309. void CComboBox::x_ButtonClicked(void)
  310. {
  311.     if (m_PopupWin  &&  ((Fl_Widget*)m_PopupWin)->visible()) {
  312.         x_CloseBrowser();
  313.     } else {
  314.         x_OpenBrowser();
  315.     }
  316. }
  317. //-----------------------------------------------------------------------------
  318. // PRE : a key pressed in input box
  319. // POST: if something was entered at the end of the input box, looks for
  320. // matches in the entry list; if a match is found, the completion is
  321. // displayed in the input box (selected, so that the user can easily delete
  322. // it if desired)
  323. void CComboBox::x_Keypressed(void)
  324. {
  325.     if (m_InKeypressed  ||  m_Input->size() == 0  ||
  326.         (!(type() & eRestrictToEntries)  &&
  327.          (m_Input->position() != m_Input->size()  ||
  328.           Fl::event_key() == FL_Delete  ||
  329.           Fl::event_key() == FL_BackSpace))) {
  330.         return;
  331.     }
  332.     m_InKeypressed = true;  // recursive call guard
  333.     CMap::const_iterator matchI =
  334.         m_EntryMap->FindInitial(m_Input->value(),
  335.                                 (type() & eCaseSensitive) > 0);
  336.     if (matchI != m_EntryMap->end()) {
  337.         const char* match = m_Entries[matchI->second].c_str();
  338.         const char* input = m_Input->value();
  339.         if (!(type() & eCaseSensitive)) {
  340.             for (;  toupper(*input) == toupper(*match)  &&  *match != '';
  341.                  ++input, ++match);
  342.             int len = match - m_Entries[matchI->second].c_str();
  343.             int pos = m_Input->position();
  344.             m_Input->replace(0,len, m_Entries[matchI->second].c_str(), len);
  345.             m_Input->position(pos);
  346.         } else {
  347.             for (;  *input == *match  &&  *match != '';  ++input, ++match);
  348.         }
  349.         if (*match != '') {
  350.             int pos = m_Input->position();
  351.             if (Fl::event_key() == FL_BackSpace) {
  352.                 --pos;
  353.             }
  354.             m_Input->insert(match);
  355.             m_Input->position(pos, m_Input->size());
  356.             m_Browser->value(matchI->second+1);
  357.         }
  358.     } else if (type() & eRestrictToEntries) { //if restricting & no match..
  359.         m_Input->Undo();
  360.     }
  361.     m_InKeypressed = false; // recursive call guard
  362. }
  363. //-----------------------------------------------------------------------------
  364. // PRE : user selected an item in the browser
  365. // POST: input box displays text of this item
  366. void CComboBox::x_ItemSelected(void)
  367. {
  368.     if (m_Browser->value() > 0) {
  369.         m_Input->value(m_Entries[m_Browser->value()-1].c_str());
  370.         if (Fl::event() == FL_RELEASE  &&  Fl::event_inside(m_Browser)) {
  371.             x_CloseBrowser();
  372.         }
  373.     }
  374. }
  375. //-----------------------------------------------------------------------------
  376. // PRE : none
  377. // POST: browser window open with ??? displayed
  378. void CComboBox::x_OpenBrowser(void)
  379. {
  380.     Fl::belowmouse(this);//hack: FLTK won't change belowmouse when grabbing
  381.     int boxY, browserY;
  382.     //adjust m_PopupWin *BEFORE* adding controls
  383.     if (y() + Fl::event_y_root()-Fl::event_y() + m_Box->h()+m_Browser->h() <
  384.         Fl::h()) {//drop "down"
  385.         boxY     = 0;
  386.         browserY = m_Box->h();
  387.         m_PopupWin->resize(x() + Fl::event_x_root()-Fl::event_x(),
  388.                            y() + Fl::event_y_root()-Fl::event_y(),
  389.                            w(), m_Box->h()+m_Browser->h());
  390.     } else { //drop "up"
  391.         boxY     = m_Browser->h();
  392.         browserY = 0;
  393.         m_PopupWin->resize(x() + Fl::event_x_root()-Fl::event_x(),
  394.                            y() + Fl::event_y_root()-Fl::event_y()
  395.                            - m_Browser->h(),
  396.                            w(), m_Box->h()+m_Browser->h());
  397.     }
  398.     m_PopupWin->add(m_Box);
  399.     m_PopupWin->add(m_Input);
  400.     m_PopupWin->add(m_Button);
  401.     m_Box->position(0, boxY);
  402.     ((Fl_Widget*) m_Input)->position(Fl::box_dx(FL_DOWN_FRAME),
  403.                                      boxY+Fl::box_dy(FL_DOWN_FRAME));
  404.     m_Button->position(m_Input->x()+m_Input->w()+1, m_Input->y());
  405.     ((Fl_Widget*) m_Browser)->resize(0, browserY, w(),
  406.                                      m_PopupWin->h()-m_Box->h());
  407.     m_PopupWin->show();
  408.     if (Fl::focus() != m_Input) {
  409.         Fl::focus(m_Input);
  410.     }
  411.     Fl::grab(m_PopupWin);
  412. }
  413. //-----------------------------------------------------------------------------
  414. // PRE : browser window open
  415. // POST: browser window closed
  416. void CComboBox::x_CloseBrowser(void)
  417. {
  418.     //order is crucial: move widget *before* hiding popup
  419.     add(m_Box);
  420.     add(m_Input);
  421.     add(m_Button);
  422.     m_Box->position(x(),y());
  423.     ((Fl_Widget*)m_Input)->position(x() + Fl::box_dx(FL_DOWN_FRAME),
  424.                                     y() + Fl::box_dy(FL_DOWN_FRAME));
  425.     m_Button->position(m_Input->x() + m_Input->w()+1, m_Input->y());
  426.     m_ResizeBox->resize(m_Input->x(), m_Input->y(), m_Input->w(), 1);
  427.     m_PopupWin->hide();
  428.     Fl::grab(0);
  429.     x_MaybeCallback();
  430. }
  431. //-----------------------------------------------------------------------------
  432. // PRE : type flags -- binary or of ETypeFlags
  433. // POST: 
  434. void CComboBox::type(unsigned char t)
  435. {
  436.     if ((t & eRestrictToEntries)  &&
  437.         value() != ""  &&  value_index() == -1) {
  438.         value("");
  439.     }
  440.     if (!(t & eCaseSensitive)  &&  (type() & eCaseSensitive)) {
  441.         // Cycle through & upcase map
  442.         CMap *newMap = new CMap;
  443.         ITERATE(CMap, mapI, *m_EntryMap) {
  444.             newMap->Add(mapI->first.c_str(), mapI->second, false);
  445.         }
  446.         m_EntryMap.reset(newMap);
  447.     }
  448.     Fl_Widget::type(t);
  449. }
  450. //-----------------------------------------------------------------------------
  451. // PRE : an entry & an (optional) abbreviation for entry
  452. // POST: if not already there, entry & abbreviation added to combobox list
  453. // of possible options
  454. void CComboBox::Add(const char* entry, const char* abbrev)
  455. {
  456.     string s(entry);
  457.     if (!(type() & eCaseSensitive)) {
  458.         NStr::ToUpper(s);
  459.     }
  460.     CMap::iterator it = m_EntryMap->lower_bound(s);
  461.     if (it == m_EntryMap->end()  ||
  462.         it->first != s) {
  463.         m_Browser->add(entry);
  464.         m_Entries.push_back(entry);
  465.         it = m_EntryMap->insert(it, CMap::value_type(s, m_Entries.size()-1));
  466.     }
  467.     if (abbrev) {
  468.         m_EntryMap->Add(abbrev, it->second, (type() & eCaseSensitive) > 0);
  469.     }
  470. }
  471. //-----------------------------------------------------------------------------
  472. // PRE : none
  473. // POST: browser list & input box cleared
  474. void CComboBox::clear(void)
  475. {
  476.     m_Browser->clear();
  477.     m_Entries.clear();
  478.     m_EntryMap->clear();
  479.     m_Input->value("");
  480. }
  481. END_NCBI_SCOPE
  482. /*
  483.  * ===========================================================================
  484.  * $Log: combobox.cpp,v $
  485.  * Revision 1000.3  2004/06/01 21:08:58  gouriano
  486.  * PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.16
  487.  *
  488.  * Revision 1.16  2004/05/28 14:10:51  johnson
  489.  * Enhanced performance of Add method
  490.  *
  491.  * Revision 1.15  2004/05/21 22:27:53  gorelenk
  492.  * Added PCH ncbi_pch.hpp
  493.  *
  494.  * Revision 1.14  2004/05/13 17:06:29  johnson
  495.  * fix to get drop up working on first click
  496.  *
  497.  * Revision 1.13  2004/05/12 20:43:57  johnson
  498.  * drop "up" when near bottom of screen
  499.  *
  500.  * Revision 1.12  2004/02/09 16:49:58  johnson
  501.  * added popup_height get/set methods
  502.  *
  503.  * Revision 1.11  2004/01/21 23:55:40  johnson
  504.  * fix to get popup working correctly after resize
  505.  *
  506.  * Revision 1.10  2004/01/13 21:15:28  johnson
  507.  * made popup a top-level window so it will appear even if combobox is at the
  508.  * bottom of the parent window
  509.  *
  510.  * Revision 1.9  2003/11/19 22:15:09  johnson
  511.  * don't swallow enter unnecessarily
  512.  *
  513.  * Revision 1.8  2003/11/05 20:35:11  johnson
  514.  * fixed bug introduced in rev 1.7; '>' is of higher precedence than '&'.
  515.  *
  516.  * Revision 1.7  2003/10/30 15:58:26  ivanov
  517.  * Get rid of compilation warnings
  518.  *
  519.  * Revision 1.6  2003/10/14 22:51:47  johnson
  520.  * added method to clear browser; tweaked response to backspace
  521.  *
  522.  * Revision 1.5  2003/08/29 18:29:38  johnson
  523.  * Numerous tweaks & adjustments: replaced Fl_Input with class that does *not*
  524.  * automatically swallows ctrl key shortcuts; case in/sensitive matching;
  525.  * multiple abbreviations for a given entry; smoothed internal access to map
  526.  *
  527.  * Revision 1.4  2003/08/26 15:53:58  johnson
  528.  * adjusted value setter to select the correct browser element
  529.  *
  530.  * Revision 1.3  2003/08/26 13:20:16  johnson
  531.  * added value set function; slight modification of up & down arrow behavior
  532.  *
  533.  * Revision 1.2  2003/08/21 12:24:13  dicuccio
  534.  * Added to NCBI namespace
  535.  *
  536.  * Revision 1.1  2003/08/19 18:18:00  johnson
  537.  * initial revision
  538.  *
  539.  * ===========================================================================
  540.  */