combobox.cpp
上传用户:yhdzpy8989
上传日期:2007-06-13
资源大小:13604k
文件大小:19k
- /*
- * ===========================================================================
- * PRODUCTION $Log: combobox.cpp,v $
- * PRODUCTION Revision 1000.3 2004/06/01 21:08:58 gouriano
- * PRODUCTION PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.16
- * PRODUCTION
- * ===========================================================================
- */
- /* $Id: combobox.cpp,v 1000.3 2004/06/01 21:08:58 gouriano Exp $
- * ===========================================================================
- *
- * PUBLIC DOMAIN NOTICE
- * National Center for Biotechnology Information
- *
- * This software/database is a "United States Government Work" under the
- * terms of the United States Copyright Act. It was written as part of
- * the author's official duties as a United States Government employee and
- * thus cannot be copyrighted. This software/database is freely available
- * to the public for use. The National Library of Medicine and the U.S.
- * Government have not placed any restriction on its use or reproduction.
- *
- * Although all reasonable efforts have been taken to ensure the accuracy
- * and reliability of the software and data, the NLM and the U.S.
- * Government do not and cannot warrant the performance or results that
- * may be obtained by using this software or data. The NLM and the U.S.
- * Government disclaim all warranties, express or implied, including
- * warranties of performance, merchantability or fitness for any particular
- * purpose.
- *
- * Please cite the author in any work or product based on this material.
- *
- * ===========================================================================
- *
- * Author: Philip Johnson
- *
- * File Description: combobox.cpp -- dropdown combobox widget for FLTK
- *
- */
- #include <ncbi_pch.hpp>
- #include <corelib/ncbistl.hpp>
- #include <gui/widgets/fl/combobox.hpp>
- #include <FL/Fl_Box.H>
- #include <FL/Fl_Button.H>
- #include <FL/Fl_Input.H>
- BEGIN_NCBI_SCOPE
- //-----------------------------------------------------------------------------
- // PRE : undo information stored in member variables
- // POST: the previous value, position & mark have been restored
- void CComboBox::CInput::Undo(void)
- {
- value(m_Value.c_str());
- position(m_Position, m_Mark);
- }
- //-----------------------------------------------------------------------------
- // PRE : input event
- // POST: if ctrl pressed, parent window is checked for shortcuts; current
- // input information (value, mark, position) is stored for undo purposes
- int CComboBox::CInput::handle(int e)
- {
- if (e == FL_KEYBOARD) {
- if ((Fl::event_state() & FL_CTRL) &&
- window()->handle(FL_SHORTCUT)) { //check for shortcuts
- return 1;
- }
- m_Value = value();
- m_Mark = mark();
- m_Position = position();
- }
- return Fl_Input::handle(e);
- }
- //-----------------------------------------------------------------------------
- // PRE : browser event
- // POST: standard browser event handler; if value() is 0 & down arrow
- // pressed, value changed to 1 (bug in std handler changes the value to 2)
- int CComboBox::CBrowser::handle(int e)
- {
- if (e == FL_KEYBOARD && Fl::event_key() == FL_Down && value() == 0) {
- value(1);
- do_callback();
- return 1;
- }
- return Fl_Hold_Browser::handle(e);
- }
- //-----------------------------------------------------------------------------
- // PRE : a widget
- // POST: whether the current event *ROOT* is found within the box specified
- // by the widget
- bool EventRootInside(Fl_Widget *w) {
- if (!w) {
- return false;
- }
- return !(Fl::event_x_root() < w->x() ||
- Fl::event_x_root() >= w->x() + w->w() ||
- Fl::event_y_root() < w->y() ||
- Fl::event_y_root() >= w->y() + w->h());
- }
- //-----------------------------------------------------------------------------
- // PRE : standard fltk boundaries
- // POST: popupwin created
- CComboBox::CPopupWin::CPopupWin(int x, int y, int w, int h)
- : Fl_Menu_Window(x,y,w,h), m_MouseOver(NULL)
- {
- clear_border();
- size_range(1,1);
- }
- //-----------------------------------------------------------------------------
- // PRE : a grabbed event
- // POST: event passed to leaf widgets as appropriate
- int CComboBox::CPopupWin::handle(int event)
- {
- switch (event) {
- case FL_SHOW:
- m_MouseOver = NULL;
- break;
- case FL_MOVE:
- {
- // We moved outside the top-level window; close it!
- Fl_Window *top = Fl::first_window();
- if (top == this) {
- top = Fl::next_window(top);
- }
- if (!EventRootInside(top) &&
- !EventRootInside(this)) {
- do_callback();
- return 1;
- }
- }
- {
- // Identify & process leave events
- int childI;
- for (childI = children() - 1;
- childI >= 0 && !Fl::event_inside(child(childI));
- --childI);
-
- if (childI < 0) {
- if (m_MouseOver) {
- m_MouseOver->handle(FL_LEAVE);
- m_MouseOver = NULL;
- }
- } else if (child(childI) != m_MouseOver) {
- if (m_MouseOver) {
- m_MouseOver->handle(FL_LEAVE);
- }
- m_MouseOver = child(childI);
- }
- }
- break;
- case FL_PUSH:
- if (Fl::event_x() < 0 || Fl::event_x() >= w() ||
- Fl::event_y() < 0 || Fl::event_y() >= h()) {
- do_callback();
- return 1;
- }
- break;
- case FL_KEYBOARD:
- case FL_SHORTCUT:
- switch(Fl::event_key()) {
- case FL_Tab:
- case FL_Enter:
- case FL_Escape:
- case FL_Control_L:
- case FL_Alt_L:
- case FL_Alt_R:
- do_callback();
- return 0;
- default: break;
- }
- for (int childI = children()-1; childI >= 0; --childI) {
- if (child(childI)->handle(event) != 0) {
- return 1;
- }
- }
- break;
- default: break;
- }
- return Fl_Menu_Window::handle(event);
- }
- //-----------------------------------------------------------------------------
- // PRE :
- // POST:
- CComboBox::CComboBox(int x, int y, int w, int h, const char* label)
- : Fl_Group(x,y,w,h+1, label), m_InKeypressed(false), m_EntryMap(new CMap)
- {
- when(FL_WHEN_CHANGED);
-
- int dx = Fl::box_dx(FL_DOWN_FRAME);
- int dy = Fl::box_dy(FL_DOWN_FRAME);
- int buttonD = h-2*dy;
- {
- m_Box = new Fl_Box(FL_DOWN_FRAME, x,y,w,h, NULL);
- m_Input = new CInput(x+dx, y+dy, w-2*dx-buttonD-1, buttonD);
- m_Input->box(FL_FLAT_BOX);
- m_Input->when(FL_WHEN_CHANGED);
- m_Input->callback((Fl_Callback*) s_InputCB, this);
-
- m_Button = new Fl_Button(x+(w-dx-buttonD), y+dy, buttonD,buttonD);
- m_Button->clear_visible_focus();
- m_Button->label("@-12>");
- m_Button->callback((Fl_Callback*) s_ButtonCB, this);
- }
- // Hidden window, mimics group
- Fl_Group::current(0);
- m_PopupWin = new CPopupWin(x, y, w, h*4);
- {
- m_Browser = new CBrowser(0, h, w, h*3);
- m_Browser->box(FL_BORDER_BOX);
- m_Browser->callback((Fl_Callback*) s_BrowserCB, this);
- }
- begin();
- m_PopupWin->resizable(m_PopupWin);
- m_PopupWin->callback((Fl_Callback*) s_CloseCB, this);
- m_PopupWin->end();
- m_PopupWin->hide();
- m_ResizeBox = new Fl_Box(m_Input->x(), m_Input->y(), m_Input->w(), 1);
- m_ResizeBox->hide();
- resizable(m_ResizeBox);
- end();
- }
- //-----------------------------------------------------------------------------
- // PRE : event for whole combobox
- // POST: open browser on down arrow; close browser on escape
- int CComboBox::handle(int event)
- {
- switch (event) {
- case FL_KEYBOARD:
- switch (Fl::event_key()) {
- case FL_Down:
- case FL_Up:
- {
- int curr = value_index();
- if (curr == -1 ||
- !((m_Input->mark() == 0 &&
- m_Input->position() == m_Input->size()) ||
- (m_Input->position() == 0 &&
- m_Input->mark() == m_Input->size()))) {
- x_OpenBrowser();
- } else {
- int next = (Fl::event_key() == FL_Down) ? curr+1 : curr-1;
- if (next >= 0 && next < (int) m_Entries.size()) {
- value(m_Entries[next].c_str());
- }
- }
- return 1;
- }
- case FL_Enter:
- x_MaybeCallback();
- default:
- break;
- }
- break;
- case FL_UNFOCUS:
- x_MaybeCallback();
- break;
- default:
- break;
- }
- return Fl_Group::handle(event);
- };
- //-----------------------------------------------------------------------------
- // PRE : new value for combobox
- // POST: input box set to that value; browser scrolled to it as, if possible
- void CComboBox::value(const char* s)
- {
- m_Input->value(s);
- int idx = value_index();
- if (idx != -1) {
- m_Browser->value(idx+1);
- }
- }
- //-----------------------------------------------------------------------------
- // PRE : none
- // POST: the current value of the combobox
- const char* CComboBox::value(void) const
- {
- return m_Input->value();
- }
- //-----------------------------------------------------------------------------
- // PRE : none
- // POST: the index of the current value in the entry list (or -1 if not found)
- int CComboBox::value_index(void) const
- {
- CMap::const_iterator i =
- m_EntryMap->FindExact(m_Input->value(), (type() & eCaseSensitive) > 0);
- if (i == m_EntryMap->end()) {
- return -1;
- } else {
- return i->second;
- }
- }
- //-----------------------------------------------------------------------------
- // PRE : conditions ripe for potential callback
- // POST: callback executed, depending on WHEN mask
- void CComboBox::x_MaybeCallback(void)
- {
- if ((when() & FL_WHEN_NOT_CHANGED) ||
- ((when() & FL_WHEN_CHANGED) &&
- m_PrevValue != m_Input->value())) {
- if (when() & FL_WHEN_RELEASE_ALWAYS) {
- do_callback();
- } else if (value_index() != -1) {
- do_callback();
- }
- }
- m_PrevValue = m_Input->value();
- }
- //-----------------------------------------------------------------------------
- // PRE : down-arrow button clicked
- // POST: if browser was open, it's now closed; if it was closed, it's now open
- void CComboBox::x_ButtonClicked(void)
- {
- if (m_PopupWin && ((Fl_Widget*)m_PopupWin)->visible()) {
- x_CloseBrowser();
- } else {
- x_OpenBrowser();
- }
- }
- //-----------------------------------------------------------------------------
- // PRE : a key pressed in input box
- // POST: if something was entered at the end of the input box, looks for
- // matches in the entry list; if a match is found, the completion is
- // displayed in the input box (selected, so that the user can easily delete
- // it if desired)
- void CComboBox::x_Keypressed(void)
- {
- if (m_InKeypressed || m_Input->size() == 0 ||
- (!(type() & eRestrictToEntries) &&
- (m_Input->position() != m_Input->size() ||
- Fl::event_key() == FL_Delete ||
- Fl::event_key() == FL_BackSpace))) {
- return;
- }
- m_InKeypressed = true; // recursive call guard
- CMap::const_iterator matchI =
- m_EntryMap->FindInitial(m_Input->value(),
- (type() & eCaseSensitive) > 0);
- if (matchI != m_EntryMap->end()) {
- const char* match = m_Entries[matchI->second].c_str();
- const char* input = m_Input->value();
- if (!(type() & eCaseSensitive)) {
- for (; toupper(*input) == toupper(*match) && *match != ' ';
- ++input, ++match);
- int len = match - m_Entries[matchI->second].c_str();
- int pos = m_Input->position();
- m_Input->replace(0,len, m_Entries[matchI->second].c_str(), len);
- m_Input->position(pos);
- } else {
- for (; *input == *match && *match != ' '; ++input, ++match);
- }
- if (*match != ' ') {
- int pos = m_Input->position();
- if (Fl::event_key() == FL_BackSpace) {
- --pos;
- }
- m_Input->insert(match);
- m_Input->position(pos, m_Input->size());
- m_Browser->value(matchI->second+1);
- }
- } else if (type() & eRestrictToEntries) { //if restricting & no match..
- m_Input->Undo();
- }
- m_InKeypressed = false; // recursive call guard
- }
- //-----------------------------------------------------------------------------
- // PRE : user selected an item in the browser
- // POST: input box displays text of this item
- void CComboBox::x_ItemSelected(void)
- {
- if (m_Browser->value() > 0) {
- m_Input->value(m_Entries[m_Browser->value()-1].c_str());
- if (Fl::event() == FL_RELEASE && Fl::event_inside(m_Browser)) {
- x_CloseBrowser();
- }
- }
- }
- //-----------------------------------------------------------------------------
- // PRE : none
- // POST: browser window open with ??? displayed
- void CComboBox::x_OpenBrowser(void)
- {
- Fl::belowmouse(this);//hack: FLTK won't change belowmouse when grabbing
- int boxY, browserY;
- //adjust m_PopupWin *BEFORE* adding controls
- if (y() + Fl::event_y_root()-Fl::event_y() + m_Box->h()+m_Browser->h() <
- Fl::h()) {//drop "down"
- boxY = 0;
- browserY = m_Box->h();
- m_PopupWin->resize(x() + Fl::event_x_root()-Fl::event_x(),
- y() + Fl::event_y_root()-Fl::event_y(),
- w(), m_Box->h()+m_Browser->h());
- } else { //drop "up"
- boxY = m_Browser->h();
- browserY = 0;
- m_PopupWin->resize(x() + Fl::event_x_root()-Fl::event_x(),
- y() + Fl::event_y_root()-Fl::event_y()
- - m_Browser->h(),
- w(), m_Box->h()+m_Browser->h());
- }
- m_PopupWin->add(m_Box);
- m_PopupWin->add(m_Input);
- m_PopupWin->add(m_Button);
- m_Box->position(0, boxY);
- ((Fl_Widget*) m_Input)->position(Fl::box_dx(FL_DOWN_FRAME),
- boxY+Fl::box_dy(FL_DOWN_FRAME));
- m_Button->position(m_Input->x()+m_Input->w()+1, m_Input->y());
- ((Fl_Widget*) m_Browser)->resize(0, browserY, w(),
- m_PopupWin->h()-m_Box->h());
- m_PopupWin->show();
- if (Fl::focus() != m_Input) {
- Fl::focus(m_Input);
- }
- Fl::grab(m_PopupWin);
- }
- //-----------------------------------------------------------------------------
- // PRE : browser window open
- // POST: browser window closed
- void CComboBox::x_CloseBrowser(void)
- {
- //order is crucial: move widget *before* hiding popup
- add(m_Box);
- add(m_Input);
- add(m_Button);
- m_Box->position(x(),y());
- ((Fl_Widget*)m_Input)->position(x() + Fl::box_dx(FL_DOWN_FRAME),
- y() + Fl::box_dy(FL_DOWN_FRAME));
- m_Button->position(m_Input->x() + m_Input->w()+1, m_Input->y());
- m_ResizeBox->resize(m_Input->x(), m_Input->y(), m_Input->w(), 1);
- m_PopupWin->hide();
- Fl::grab(0);
- x_MaybeCallback();
- }
- //-----------------------------------------------------------------------------
- // PRE : type flags -- binary or of ETypeFlags
- // POST:
- void CComboBox::type(unsigned char t)
- {
- if ((t & eRestrictToEntries) &&
- value() != "" && value_index() == -1) {
- value("");
- }
- if (!(t & eCaseSensitive) && (type() & eCaseSensitive)) {
- // Cycle through & upcase map
- CMap *newMap = new CMap;
- ITERATE(CMap, mapI, *m_EntryMap) {
- newMap->Add(mapI->first.c_str(), mapI->second, false);
- }
- m_EntryMap.reset(newMap);
- }
- Fl_Widget::type(t);
- }
- //-----------------------------------------------------------------------------
- // PRE : an entry & an (optional) abbreviation for entry
- // POST: if not already there, entry & abbreviation added to combobox list
- // of possible options
- void CComboBox::Add(const char* entry, const char* abbrev)
- {
- string s(entry);
- if (!(type() & eCaseSensitive)) {
- NStr::ToUpper(s);
- }
- CMap::iterator it = m_EntryMap->lower_bound(s);
- if (it == m_EntryMap->end() ||
- it->first != s) {
- m_Browser->add(entry);
- m_Entries.push_back(entry);
- it = m_EntryMap->insert(it, CMap::value_type(s, m_Entries.size()-1));
- }
- if (abbrev) {
- m_EntryMap->Add(abbrev, it->second, (type() & eCaseSensitive) > 0);
- }
- }
- //-----------------------------------------------------------------------------
- // PRE : none
- // POST: browser list & input box cleared
- void CComboBox::clear(void)
- {
- m_Browser->clear();
- m_Entries.clear();
- m_EntryMap->clear();
- m_Input->value("");
- }
- END_NCBI_SCOPE
- /*
- * ===========================================================================
- * $Log: combobox.cpp,v $
- * Revision 1000.3 2004/06/01 21:08:58 gouriano
- * PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.16
- *
- * Revision 1.16 2004/05/28 14:10:51 johnson
- * Enhanced performance of Add method
- *
- * Revision 1.15 2004/05/21 22:27:53 gorelenk
- * Added PCH ncbi_pch.hpp
- *
- * Revision 1.14 2004/05/13 17:06:29 johnson
- * fix to get drop up working on first click
- *
- * Revision 1.13 2004/05/12 20:43:57 johnson
- * drop "up" when near bottom of screen
- *
- * Revision 1.12 2004/02/09 16:49:58 johnson
- * added popup_height get/set methods
- *
- * Revision 1.11 2004/01/21 23:55:40 johnson
- * fix to get popup working correctly after resize
- *
- * Revision 1.10 2004/01/13 21:15:28 johnson
- * made popup a top-level window so it will appear even if combobox is at the
- * bottom of the parent window
- *
- * Revision 1.9 2003/11/19 22:15:09 johnson
- * don't swallow enter unnecessarily
- *
- * Revision 1.8 2003/11/05 20:35:11 johnson
- * fixed bug introduced in rev 1.7; '>' is of higher precedence than '&'.
- *
- * Revision 1.7 2003/10/30 15:58:26 ivanov
- * Get rid of compilation warnings
- *
- * Revision 1.6 2003/10/14 22:51:47 johnson
- * added method to clear browser; tweaked response to backspace
- *
- * Revision 1.5 2003/08/29 18:29:38 johnson
- * Numerous tweaks & adjustments: replaced Fl_Input with class that does *not*
- * automatically swallows ctrl key shortcuts; case in/sensitive matching;
- * multiple abbreviations for a given entry; smoothed internal access to map
- *
- * Revision 1.4 2003/08/26 15:53:58 johnson
- * adjusted value setter to select the correct browser element
- *
- * Revision 1.3 2003/08/26 13:20:16 johnson
- * added value set function; slight modification of up & down arrow behavior
- *
- * Revision 1.2 2003/08/21 12:24:13 dicuccio
- * Added to NCBI namespace
- *
- * Revision 1.1 2003/08/19 18:18:00 johnson
- * initial revision
- *
- * ===========================================================================
- */