// -*- C++ -*- /* * Combox: A combination of two objects (a button and a browser) is * encapsulated to get a combobox-like object. All XForms * functions are hidden. * * GNU Copyleft 1996 Alejandro Aguilar Sierra * and the LyX Team. * * Dependencies: Only XForms, but created to be used with LyX. * */ /* Change log: * * 2/06/1996, Alejandro Aguilar Sierra * Created and tested. * * 4/06/1996, Alejandro Aguilar Sierra * Added droplist mode (a button with a black down arrow at right) * and support for middle and right buttons, as XForms choice object. * * 6/06/1996, Lars Gullik Bjønnes * Added a combox with an input object. and a pre and a post handle. * * 22/07/96, Alejandro Aguilar Sierra * Assigned to the browser its own popup window. No more need of * external pre and post handlers to simulate the wanted behaviour. * */ #include #ifdef __GNUG__ #pragma implementation #endif #include "combox.h" #include #include "debug.h" using std::endl; // These are C wrappers around static members of Combox, used as // callbacks for xforms. extern "C" void C_Combox_input_cb(FL_OBJECT *ob, long); extern "C" void C_Combox_combo_cb(FL_OBJECT *ob, long data) ; extern "C" int C_Combox_peek_event(FL_FORM * form, void *xev); Combox::Combox(combox_type t) : type(t), tabfolder1(0), tabfolder2(0) { browser = button = 0; callback = 0; label = 0; cb_arg = 0; _pre = 0; _post = 0; sel = 0; form = 0; } Combox::~Combox() { remove(); } void Combox::clear() { if (browser) fl_clear_browser(browser); sel = 0; if (type == FL_COMBOX_INPUT) fl_set_input(label, ""); else fl_set_object_label(label, ""); is_empty = true; } void Combox::remove() { lyxerr.debug() << "Button: " << button << endl; if (button) { fl_delete_object(button); fl_free_object(button); } lyxerr.debug() << "Label: " << label << endl; if (label && label != button) { fl_delete_object(label); fl_free_object(label); } lyxerr.debug() << "Form: " << form << endl; lyxerr.debug() << "Browser: " << browser << endl; if (form && browser) { fl_delete_object(browser); fl_free_object(browser); fl_free_form(form); } button = 0; browser = 0; label = 0; form = 0; sel = 0; is_empty = true; } void Combox::addline(string const & text) { if (!browser) return; fl_add_browser_line(browser, text.c_str()); // By default the first item is selected if (!sel) { sel = 1; if (type == FL_COMBOX_INPUT) fl_set_input(label, text.c_str()); else fl_set_object_label(label, text.c_str()); } is_empty = false; } bool Combox::select_text(string const & t) { if (!browser || t.empty()) return false; int const maxline = fl_get_browser_maxline(browser); for (int i = 1; i <= maxline; ++i) { if (t == fl_get_browser_line(browser, i)) { select(i); return true; } } return false; // t does not exist in browser } void Combox::select(int i) { if (!browser || !button) return; if (i > 0 && i <= fl_get_browser_maxline(browser)) sel = i; fl_deactivate_object(button); if (type == FL_COMBOX_INPUT) fl_set_input(label, fl_get_browser_line(browser, sel)); else fl_set_object_label(label, fl_get_browser_line(browser, sel)); fl_activate_object(button); } void Combox::add(int x, int y, int w, int hmin, int hmax, FL_OBJECT * tabfolder1_, FL_OBJECT * tabfolder2_) { // Store these for later use in working round an xforms bug in Show() tabfolder1 = tabfolder1_; tabfolder2 = tabfolder2_; FL_OBJECT * obj; switch (type) { case FL_COMBOX_DROPLIST: { button = obj = fl_add_button(FL_NORMAL_BUTTON, x+w-22, y, 22, hmin, "@2->"); fl_set_object_color(obj, FL_MCOL, FL_MCOL); fl_set_object_dblbuffer(obj, 1); fl_set_object_callback(obj, C_Combox_combo_cb, 0); label = obj = fl_add_button(FL_NORMAL_TEXT, x, y, w-22, hmin, ""); fl_set_object_boxtype(obj, FL_DOWN_BOX); fl_set_object_color(obj, FL_MCOL, FL_BLACK); fl_set_object_lalign(obj, FL_ALIGN_CENTER|FL_ALIGN_INSIDE); fl_set_object_dblbuffer(obj, 1); fl_set_object_callback(obj, C_Combox_combo_cb, 0); break; } case FL_COMBOX_NORMAL: { button = obj = fl_add_button(FL_NORMAL_BUTTON, x, y, w, hmin, ""); fl_set_object_color(obj, FL_MCOL, FL_MCOL); fl_set_object_boxtype(obj, FL_DOWN_BOX); fl_set_object_callback(obj, C_Combox_combo_cb, 0); fl_set_object_color(obj, FL_MCOL, FL_BLACK); label = button; break; } case FL_COMBOX_INPUT: { button = obj = fl_add_button(FL_NORMAL_BUTTON, x+w-22, y, 22, hmin, "@2->"); fl_set_object_color(obj, FL_MCOL, FL_MCOL); fl_set_object_callback(obj, C_Combox_combo_cb, 0); label = obj = fl_add_input(FL_NORMAL_INPUT, x, y, w-22, hmin, ""); fl_set_object_boxtype(obj, FL_DOWN_BOX); fl_set_object_return(obj, FL_RETURN_END_CHANGED); fl_set_object_callback(obj, C_Combox_input_cb, 0); //fl_set_object_color(obj, FL_MCOL, FL_BLACK); //fl_set_object_lalign(obj,FL_ALIGN_CENTER|FL_ALIGN_INSIDE); break; } } // end of switch label->u_vdata = this; button->u_vdata = this; // Hmm, it seems fl_create_browser is broken in xforms 0.86. // We have to work around that by creating the dropped browser form // at this point already. However, this means that we have // to do a little hacking: (Asger) FL_FORM * current_form = fl_current_form; fl_end_form(); bw = w + 20; bh = hmax - hmin - 12; form = fl_bgn_form(FL_NO_BOX, bw, bh); browser = obj = fl_add_browser(FL_HOLD_BROWSER, 0, 0, bw, bh, ""); fl_set_object_boxtype(obj, FL_UP_BOX); fl_set_object_color(obj, FL_MCOL, FL_YELLOW); fl_set_object_gravity(obj, NorthWestGravity, NorthWestGravity); fl_set_object_callback(obj, C_Combox_combo_cb, 2); fl_end_form(); browser->u_vdata = this; form->u_vdata = browser; fl_register_raw_callback(form, ButtonPressMask|KeyPressMask, C_Combox_peek_event); // And revert to adding to the old form (Asger) fl_addto_form(current_form); } static Window save_window; void Combox::Redraw() { if (browser) fl_redraw_object(browser); if (button) fl_redraw_object(button); if (label) fl_redraw_object(label); } void Combox::Show() { if (_pre) _pre(); int tmp; XGetInputFocus(fl_get_display(), &save_window, &tmp); //BUG-Fix Dietmar XFlush(fl_get_display()); if (button && type != FL_COMBOX_NORMAL) { fl_set_object_label(button, "@2<-"); fl_redraw_object(button); } int x = label->x; int y = label->y + label->h; if (tabfolder1) { // This is a bug work around suggested by Steve Lamont on the // xforms mailing list. It correctly positions the browser form // after the main window has been moved. // The bug only occurs in tabbed folders. int folder_x, folder_y, folder_w, folder_h; fl_get_folder_area( tabfolder1, &folder_x, &folder_y, &folder_w, &folder_h ); x += folder_x; y += folder_y; if (tabfolder2) { fl_get_folder_area( tabfolder2, &folder_x, &folder_y, &folder_w, &folder_h ); x += tabfolder2->form->x + folder_x; y += tabfolder2->form->y + folder_y; } else { x += tabfolder1->form->x; y += tabfolder1->form->y; } } else { x += label->form->x; y += label->form->y; } fl_set_form_position(form, x, y); fl_show_form(form, FL_PLACE_POSITION, FL_NOBORDER, ""); if (sel>0) { fl_set_browser_topline(browser, sel); fl_select_browser_line(browser, sel); } XGrabPointer(fl_get_display(), form->window, false, ButtonPressMask | ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, 0, 0, 0); XFlush(fl_get_display()); } void Combox::Hide(int who) { if (!who && browser && label) { sel = fl_get_browser(browser); if (type == FL_COMBOX_INPUT) fl_set_input(label, fl_get_browser_line(browser, sel)); else fl_set_object_label(label, fl_get_browser_line(browser, sel)); // if (callback) callback(sel, cb_arg); } XUngrabPointer(fl_get_display(), 0); XFlush(fl_get_display()); if (!who && browser && label && callback) callback(sel, cb_arg, this); if (form) { fl_hide_form(form); XSetInputFocus(fl_get_display(), save_window, RevertToParent, CurrentTime); // BUG-FIX-Dietmar XFlush(fl_get_display()); } if (button) { if (type != FL_COMBOX_NORMAL){ fl_set_object_label(button, "@2->"); fl_redraw_object(button); } } if (_post) _post(); } void Combox::activate() { if (browser) fl_activate_object(browser); if (button) fl_activate_object(button); if (label) fl_activate_object(label); } void Combox::deactivate() { if (browser) fl_deactivate_object(browser); if (button) fl_deactivate_object(button); if (label) fl_deactivate_object(label); } void Combox::input_cb(FL_OBJECT * ob, long) { Combox * combo = static_cast(ob->u_vdata); char const * text = fl_get_input(ob); combo->addto(text ? string(text) : string()); combo->is_empty = false; } extern "C" void C_Combox_input_cb(FL_OBJECT * ob, long data) { Combox::input_cb(ob, data); } void Combox::combo_cb(FL_OBJECT * ob, long data) { Combox * combo = static_cast(ob->u_vdata); switch (data) { case 0: { int const i = combo->get(); switch (fl_get_button_numb(ob)) { case 2: { combo->select(i - 1); if (combo->callback) combo->callback(combo->sel, combo->cb_arg, combo); break; } case 3: { combo->select(i + 1); if (combo->callback) combo->callback(combo->sel, combo->cb_arg, combo); break; } default: combo->Show(); break; } break; } case 2: combo->Hide(); break; } } extern "C" void C_Combox_combo_cb(FL_OBJECT * ob, long data) { Combox::combo_cb(ob, data); } int Combox::peek_event(FL_FORM * form, void * xev) { FL_OBJECT * ob = static_cast(form->u_vdata); Combox * combo = static_cast(ob->u_vdata); // I don't know why belowmouse does not work, but it doesn't. (Asger) // Are we sure? Please verify. (Lgb) if (static_cast(xev)->type == ButtonPress && ( static_cast(xev)->xbutton.x - ob->x < 0 || static_cast(xev)->xbutton.x - ob->x > ob->w || static_cast(xev)->xbutton.y - ob->y < 0 || static_cast(xev)->xbutton.y - ob->y > ob->h)) { combo->Hide(1); return 1; } if (static_cast(xev)->type != KeyPress) return 0; char s_r[10]; s_r[9] = '\0'; KeySym keysym_return; XLookupString(&static_cast(xev)->xkey, s_r, 10, &keysym_return, 0); XFlush(fl_get_display()); switch (keysym_return) { case XK_Down: if (fl_get_browser(combo->browser) < fl_get_browser_maxline(combo->browser)) fl_select_browser_line(combo->browser, fl_get_browser(combo->browser)+1); if (fl_get_browser(combo->browser)>= fl_get_browser_topline(combo->browser) + fl_get_browser_screenlines(combo->browser)) fl_set_browser_topline(combo->browser, fl_get_browser(combo->browser) - fl_get_browser_screenlines(combo->browser)+1); if (fl_get_browser(combo->browser)< fl_get_browser_topline(combo->browser)) fl_set_browser_topline(combo->browser, fl_get_browser(combo->browser)); return 1; case XK_Up: if (fl_get_browser(combo->browser) > 1) fl_select_browser_line(combo->browser, fl_get_browser(combo->browser)-1); if (fl_get_browser(combo->browser)>= fl_get_browser_topline(combo->browser) + fl_get_browser_screenlines(combo->browser)) fl_set_browser_topline(combo->browser, fl_get_browser(combo->browser) - fl_get_browser_screenlines(combo->browser)+1); if (fl_get_browser(combo->browser) < fl_get_browser_topline(combo->browser)) fl_set_browser_topline(combo->browser, fl_get_browser(combo->browser)); return 1; case XK_Return: combo->Hide(); return 1; case XK_Escape: combo->Hide(1); return 1; } return 0; } extern "C" int C_Combox_peek_event(FL_FORM * form, void *xev) { return Combox::peek_event(form, xev); } #ifdef TESTCOMBO typedef struct { FL_FORM *test; FL_OBJECT *bar; void *vdata; long ldata; } FD_test; //Combox combo(FL_COMBOX_DROPLIST); Combox combo(FL_COMBOX_INPUT); FD_test *fd_test; FD_test *create_form_test(void) { FL_OBJECT *obj; FD_test *fdui = (FD_test *) fl_calloc(1, sizeof(*fdui)); fdui->test = fl_bgn_form(FL_NO_BOX, 320, 190); obj = fl_add_box(FL_UP_BOX, 0, 0, 320, 190, ""); obj = fl_add_box(FL_DOWN_BOX, 10, 50, 300, 110, ""); obj = fl_add_button(FL_NORMAL_BUTTON, 250, 10, 50, 30, _("Done")); combo.add(10, 15, 120, 25, 135); fl_end_form(); return fdui; } void combo_cb(int i) { fprintf(stderr, "selected %d:%s\n", i, combo.getline()); } int main(int argc, char *argv[]) { //int n1; // Same defaults as in lyx FL_IOPT cntl; cntl.buttonFontSize = FL_NORMAL_SIZE; cntl.browserFontSize = FL_NORMAL_SIZE; cntl.labelFontSize = FL_NORMAL_SIZE; cntl.choiceFontSize = FL_NORMAL_SIZE; cntl.inputFontSize = FL_NORMAL_SIZE; cntl.borderWidth = -2; fl_set_defaults(FL_PDButtonFontSize, &cntl); fl_set_defaults(FL_PDBrowserFontSize, &cntl); fl_set_defaults(FL_PDLabelFontSize, &cntl); fl_set_defaults(FL_PDChoiceFontSize, &cntl); fl_set_defaults(FL_PDInputFontSize, &cntl); fl_set_defaults(FL_PDBorderWidth, &cntl); fl_initialize(&argc, argv, 0, 0, 0); fd_test = create_form_test(); /* fill-in form initialization code */ combo.addline("Title"); combo.addline("Author"); combo.addline("Date"); combo.addline("Abstract"); combo.addline("Chapter"); combo.addline("Section"); combo.addline("Subsection"); combo.addline("List"); combo.addline("Description"); combo.addline("Verse"); combo.addline("Verbatim"); combo.setcallback(combo_cb); // combo.select(4); /* show the first form */ fl_show_form(fd_test->test, FL_PLACE_CENTER, FL_FULLBORDER, "test"); fl_do_forms(); return 0; } #endif