]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/combox.C
783116ab836f92eafafb180e99c78a6a54bae03d
[lyx.git] / src / frontends / xforms / combox.C
1 /**
2  * \file combox.C
3  * Copyright 1996Alejandro Aguilar Sierra
4  * This file is part of LyX, the document processor.
5  * Licence details can be found in the file COPYING.
6  *
7  * \author Alejandro Aguilar Sierra
8  *
9  * Full author contact details are available in file CREDITS
10  */
11
12 /* A combination of two objects (a button and a browser) is encapsulated to
13  * get a combobox-like object. All XForms functions are hidden.
14  *
15  */
16
17 /* Change log:
18  *
19  *  2/06/1996,   Alejandro Aguilar Sierra
20  *    Created and tested.
21  *
22  *  4/06/1996,   Alejandro Aguilar Sierra
23  *    Added droplist mode (a button with a black down arrow at right)
24  *    and support for middle and right buttons, as XForms choice object.
25  *
26  *  6/06/1996,   Lars Gullik Bjønnes
27  *    Added a combox with an input object. and a pre and a post handle.
28  *
29  *  22/07/96,    Alejandro Aguilar Sierra
30  *    Assigned to the browser its own popup window. No more need of
31  *    external pre and post handlers to simulate the wanted behaviour.
32  *
33  */
34
35 #include <config.h>
36
37 #ifdef __GNUG__
38 #pragma implementation
39 #endif
40
41 #include "combox.h"
42 //#include <cstring>
43
44 #include "debug.h"
45
46 using std::endl;
47
48 // These are C wrappers around static members of Combox, used as
49 // callbacks for xforms.
50 extern "C" {
51
52         static
53         void C_Combox_input_cb(FL_OBJECT * ob, long data)
54         {
55                 Combox::input_cb(ob, data);
56         }
57
58         static
59         void C_Combox_combo_cb(FL_OBJECT * ob, long data)
60         {
61                 Combox::combo_cb(ob, data);
62         }
63
64         static
65         int C_Combox_peek_event(FL_FORM * form, void *xev)
66         {
67                 return Combox::peek_event(form, xev);
68         }
69
70 }
71
72
73 Combox::Combox(combox_type t)
74         : type(t), tabfolder1(0), tabfolder2(0)
75 {
76    browser = button = 0;
77    callback = 0;
78    label = 0;
79    cb_arg = 0;
80    _pre = 0;
81    _post = 0;
82    sel = 0;
83    form = 0;
84 }
85
86
87 Combox::~Combox()
88 {
89         // This is now handled by the XFormsView destructor (Lgb)
90         // remove();
91 }
92
93
94 void Combox::clear()
95 {
96         if (browser) fl_clear_browser(browser);
97         sel = 0;
98         if (type == FL_COMBOX_INPUT)
99                 fl_set_input(label, "");
100         else
101                 fl_set_object_label(label, "");
102         is_empty = true;
103 }
104
105
106 void Combox::remove()
107 {
108         lyxerr[Debug::GUI] << "Button: " << button << endl;
109         if (button) {
110                 fl_delete_object(button);
111                 fl_free_object(button);
112         }
113
114         lyxerr[Debug::GUI] << "Label: " << label << endl;
115         if (label && label != button) {
116                 fl_delete_object(label);
117                 fl_free_object(label);
118         }
119
120         lyxerr[Debug::GUI] << "Form: " << form << endl;
121         lyxerr[Debug::GUI] << "Browser: " << browser << endl;
122         if (form && browser) {
123            fl_delete_object(browser);
124            fl_free_object(browser);
125            fl_free_form(form);
126         }
127         button = 0;
128         browser = 0;
129         label = 0;
130         form = 0;
131         sel = 0;
132         is_empty = true;
133 }
134
135
136 void Combox::addline(string const & text)
137 {
138         if (!browser) return;
139         fl_add_browser_line(browser, text.c_str());
140
141         // By default the first item is selected
142         if (!sel) {
143                 sel = 1;
144                 if (type == FL_COMBOX_INPUT)
145                         fl_set_input(label, text.c_str());
146                 else
147                         fl_set_object_label(label, text.c_str());
148         }
149         is_empty = false;
150 }
151
152
153 bool Combox::select(string const & t)
154 {
155         if (!browser || t.empty()) return false;
156         int const maxline = fl_get_browser_maxline(browser);
157
158         for (int i = 1; i <= maxline; ++i) {
159                 if (t == fl_get_browser_line(browser, i)) {
160                         select(i);
161                         return true;
162                 }
163         }
164         return false;  // t does not exist in browser
165 }
166
167
168 void Combox::select(int i)
169 {
170         if (!browser || !button) return;
171         if (i > 0 && i <= fl_get_browser_maxline(browser)) sel = i;
172         fl_deactivate_object(button);
173
174         if (type == FL_COMBOX_INPUT)
175                 fl_set_input(label, fl_get_browser_line(browser, sel));
176         else
177                 fl_set_object_label(label, fl_get_browser_line(browser, sel));
178         fl_activate_object(button);
179 }
180
181
182 void Combox::add(int x, int y, int w, int hmin, int hmax,
183                  FL_OBJECT * tabfolder1_, FL_OBJECT * tabfolder2_)
184 {
185         // Store these for later use in working round an xforms bug in show()
186         tabfolder1 = tabfolder1_;
187         tabfolder2 = tabfolder2_;
188
189         FL_OBJECT * obj;
190
191         switch (type) {
192         case FL_COMBOX_DROPLIST:
193         {
194                 button = obj = fl_add_button(FL_NORMAL_BUTTON,
195                                              x+w-22, y, 22, hmin, "@2->");
196                 fl_set_object_color(obj, FL_MCOL, FL_MCOL);
197                 fl_set_object_dblbuffer(obj, 1);
198                 fl_set_object_callback(obj, C_Combox_combo_cb, 0);
199                 label = obj = fl_add_button(FL_NORMAL_TEXT, x, y, w-22, hmin, "");
200                 fl_set_object_boxtype(obj, FL_DOWN_BOX);
201                 fl_set_object_color(obj, FL_MCOL, FL_BLACK);
202                 fl_set_object_lalign(obj, FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
203                 fl_set_object_dblbuffer(obj, 1);
204                 fl_set_object_callback(obj, C_Combox_combo_cb, 0);
205                 break;
206         }
207         case FL_COMBOX_NORMAL:
208         {
209                 button = obj = fl_add_button(FL_NORMAL_BUTTON, x, y, w, hmin, "");
210                 fl_set_object_color(obj, FL_MCOL, FL_MCOL);
211                 fl_set_object_boxtype(obj, FL_DOWN_BOX);
212                 fl_set_object_callback(obj, C_Combox_combo_cb, 0);
213                 fl_set_object_color(obj, FL_MCOL, FL_BLACK);
214                 label = button;
215                 break;
216         }
217         case FL_COMBOX_INPUT:
218         {
219                 button = obj = fl_add_button(FL_NORMAL_BUTTON,
220                                              x+w-22, y, 22, hmin, "@2->");
221                 fl_set_object_color(obj, FL_MCOL, FL_MCOL);
222                 fl_set_object_callback(obj, C_Combox_combo_cb, 0);
223                 label = obj = fl_add_input(FL_NORMAL_INPUT, x, y, w-22, hmin, "");
224                 fl_set_object_boxtype(obj, FL_DOWN_BOX);
225                 fl_set_object_return(obj, FL_RETURN_END_CHANGED);
226                 fl_set_object_callback(obj, C_Combox_input_cb, 0);
227                 //fl_set_object_color(obj, FL_MCOL, FL_BLACK);
228                 //fl_set_object_lalign(obj,FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
229                 break;
230         }
231         } // end of switch
232
233         label->u_vdata = this;
234         button->u_vdata = this;
235
236         // Hmm, it seems fl_create_browser is broken in xforms 0.86.
237         // We have to work around that by creating the dropped browser form
238         // at this point already. However, this means that we have
239         // to do a little hacking: (Asger)
240         FL_FORM * current_form = fl_current_form;
241         fl_end_form();
242
243         bw = w + 20;
244         bh = hmax - hmin - 12;
245
246         form = fl_bgn_form(FL_NO_BOX, bw, bh);
247         browser = obj = fl_add_browser(FL_HOLD_BROWSER, 0, 0, bw, bh, "");
248         fl_set_object_boxtype(obj, FL_UP_BOX);
249         fl_set_object_color(obj, FL_MCOL, FL_YELLOW);
250         fl_set_object_gravity(obj, NorthWestGravity, NorthWestGravity);
251         fl_set_object_callback(obj, C_Combox_combo_cb, 2);
252         fl_end_form();
253         browser->u_vdata = this;
254         form->u_vdata = browser;
255         fl_register_raw_callback(form,
256                                  ButtonPressMask|KeyPressMask,
257                                  C_Combox_peek_event);
258
259         // And revert to adding to the old form (Asger)
260         fl_addto_form(current_form);
261 }
262
263
264 namespace {
265
266 Window save_window;
267
268 } // namespace anon
269
270
271 void Combox::redraw()
272 {
273         if (browser) fl_redraw_object(browser);
274         if (button) fl_redraw_object(button);
275         if (label) fl_redraw_object(label);
276 }
277
278 void Combox::show()
279 {
280         if (_pre) _pre();
281
282         int tmp;
283         XGetInputFocus(fl_get_display(), &save_window, &tmp); //BUG-Fix Dietmar
284         XFlush(fl_get_display());
285         if (button && type != FL_COMBOX_NORMAL) {
286                 fl_set_object_label(button, "@2<-");
287                 fl_redraw_object(button);
288         }
289
290         int x = label->x;
291         int y = label->y + label->h;
292         if (tabfolder1) {
293                 // This is a bug work around suggested by Steve Lamont on the
294                 // xforms mailing list. It correctly positions the browser form
295                 // after the main window has been moved.
296                 // The bug only occurs in tabbed folders.
297                 int folder_x, folder_y, folder_w, folder_h;
298                 fl_get_folder_area( tabfolder1,
299                                     &folder_x, &folder_y,
300                                     &folder_w, &folder_h);
301                 x += folder_x;
302                 y += folder_y;
303
304                 if (tabfolder2) {
305                         fl_get_folder_area( tabfolder2,
306                                             &folder_x, &folder_y,
307                                             &folder_w, &folder_h);
308                         x += tabfolder2->form->x + folder_x;
309                         y += tabfolder2->form->y + folder_y;
310                 } else {
311                         x += tabfolder1->form->x;
312                         y += tabfolder1->form->y;
313                 }
314
315         } else {
316                 x += label->form->x;
317                 y += label->form->y;
318         }
319
320         fl_set_form_position(form, x, y);
321         fl_show_form(form, FL_PLACE_POSITION, FL_NOBORDER, "");
322         if (sel > 0) {
323                 fl_set_browser_topline(browser, sel);
324                 fl_select_browser_line(browser, sel);
325         }
326         XGrabPointer(fl_get_display(), form->window, false,
327                      ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
328                      GrabModeAsync, GrabModeAsync,
329                      0, 0, 0);
330         XFlush(fl_get_display());
331 }
332
333 void Combox::hide(int who)
334 {
335         if (!who && browser && label) {
336                 sel = fl_get_browser(browser);
337
338                 if (type == FL_COMBOX_INPUT)
339                         fl_set_input(label, fl_get_browser_line(browser, sel));
340                 else
341                         fl_set_object_label(label,
342                                             fl_get_browser_line(browser, sel));
343 //              if (callback) callback(sel, cb_arg);
344         }
345         XUngrabPointer(fl_get_display(), 0);
346         XFlush(fl_get_display());
347         if (form) {
348                 fl_hide_form(form);
349                 XSetInputFocus(fl_get_display(), save_window,
350                                RevertToParent, CurrentTime); // BUG-FIX-Dietmar
351                 XFlush(fl_get_display());
352         }
353         if (button) {
354                 if (type != FL_COMBOX_NORMAL) {
355                         fl_set_object_label(button, "@2->");
356                         fl_redraw_object(button);
357                 }
358         }
359         if (!who && browser && label && callback)
360             callback(sel, cb_arg, this);
361         if (_post) _post();
362 }
363
364
365 void Combox::activate()
366 {
367         if (browser)
368                 fl_activate_object(browser);
369         if (button) {
370                 fl_activate_object(button);
371                 fl_set_object_lcol(button, FL_BLACK);
372         }
373         if (label) {
374                 fl_activate_object(label);
375                 fl_set_object_lcol(label, FL_BLACK);
376         }
377 }
378
379
380 void Combox::deactivate()
381 {
382         if (browser)
383                 fl_deactivate_object(browser);
384         if (button) {
385                 fl_deactivate_object(button);
386                 fl_set_object_lcol(button, FL_INACTIVE);
387         }
388         if (label) {
389                 fl_deactivate_object(label);
390                 fl_set_object_lcol(label, FL_INACTIVE);
391         }
392 }
393
394
395 void Combox::input_cb(FL_OBJECT * ob, long)
396 {
397         Combox * combo = static_cast<Combox*>(ob->u_vdata);
398
399         char const * text = fl_get_input(ob);
400
401         combo->addto(text ? string(text) : string());
402         combo->is_empty = false;
403 }
404
405
406 void Combox::combo_cb(FL_OBJECT * ob, long data)
407 {
408         Combox * combo = static_cast<Combox*>(ob->u_vdata);
409         switch (data) {
410         case 0:
411         {
412                 int const i = combo->get();
413                 switch (fl_get_button_numb(ob)) {
414                 case 2:
415                 {
416                         combo->select(i - 1);
417                         if (combo->callback)
418                                 combo->callback(combo->sel,
419                                                 combo->cb_arg, combo);
420                         break;
421                 }
422                 case 3:
423                 {
424                         combo->select(i + 1);
425                         if (combo->callback)
426                                 combo->callback(combo->sel,
427                                                 combo->cb_arg, combo);
428                         break;
429                 }
430                 default: combo->show(); break;
431                 }
432                 break;
433         }
434         case 2:
435                 combo->hide();
436                 break;
437         }
438 }
439
440
441 int Combox::peek_event(FL_FORM * form, void * xev)
442 {
443         FL_OBJECT * ob = static_cast<FL_OBJECT *>(form->u_vdata);
444         Combox * combo = static_cast<Combox*>(ob->u_vdata);
445
446         // below mouse does not work like we need it
447         if (static_cast<XEvent *>(xev)->type == ButtonPress && (
448                 static_cast<XEvent *>(xev)->xbutton.x - ob->x < 0 ||
449                 static_cast<XEvent *>(xev)->xbutton.x - ob->x > ob->w ||
450                 static_cast<XEvent *>(xev)->xbutton.y - ob->y < 0 ||
451                 static_cast<XEvent *>(xev)->xbutton.y - ob->y > ob->h)) {
452                 combo->hide(1);
453                 return 1;
454         }
455
456         if (static_cast<XEvent*>(xev)->type != KeyPress) return 0;
457
458         char s_r[10]; s_r[9] = '\0';
459         KeySym keysym_return;
460         XLookupString(&static_cast<XEvent*>(xev)->xkey, s_r, 10,
461                               &keysym_return, 0);
462         XFlush(fl_get_display());
463         switch (keysym_return) {
464         case XK_Down:
465 #ifdef XK_KP_Down
466         case XK_KP_Down:
467 #endif
468                 if (fl_get_browser(combo->browser) <
469                     fl_get_browser_maxline(combo->browser))
470                         fl_select_browser_line(combo->browser,
471                                                fl_get_browser(combo->browser)+1);
472                 if (fl_get_browser(combo->browser)>=
473                     fl_get_browser_topline(combo->browser) +
474                     fl_get_browser_screenlines(combo->browser))
475                         fl_set_browser_topline(combo->browser,
476                                                fl_get_browser(combo->browser)
477                                                - fl_get_browser_screenlines(combo->browser)+1);
478                 if (fl_get_browser(combo->browser)<
479                     fl_get_browser_topline(combo->browser))
480                         fl_set_browser_topline(combo->browser,
481                                                fl_get_browser(combo->browser));
482                 return 1;
483         case XK_Up:
484 #ifdef XK_KP_Up
485         case XK_KP_Up:
486 #endif
487                 if (fl_get_browser(combo->browser) > 1)
488                         fl_select_browser_line(combo->browser,
489                                                fl_get_browser(combo->browser)-1);
490                 if (fl_get_browser(combo->browser)>=
491                     fl_get_browser_topline(combo->browser) +
492                     fl_get_browser_screenlines(combo->browser))
493                         fl_set_browser_topline(combo->browser,
494                                                fl_get_browser(combo->browser)
495                                                - fl_get_browser_screenlines(combo->browser)+1);
496                 if (fl_get_browser(combo->browser) <
497                     fl_get_browser_topline(combo->browser))
498                         fl_set_browser_topline(combo->browser,
499                                                fl_get_browser(combo->browser));
500                 return 1;
501         case XK_Return:
502 #ifdef XK_KP_Enter
503         case XK_KP_Enter:
504 #endif
505                 combo->hide();
506                 return 1;
507         case XK_Escape:
508                 combo->hide(1);
509                 return 1;
510         }
511         return 0;
512 }
513
514
515 #ifdef TESTCOMBO
516 typedef struct {
517         FL_FORM *test;
518         FL_OBJECT *bar;
519         void *vdata;
520         long ldata;
521 } FD_test;
522
523 //Combox combo(FL_COMBOX_DROPLIST);
524 Combox combo(FL_COMBOX_INPUT);
525
526 FD_test *fd_test;
527
528 FD_test *create_form_test(void)
529 {
530    FL_OBJECT *obj;
531    FD_test *fdui = (FD_test *) fl_calloc(1, sizeof(*fdui));
532
533    fdui->test = fl_bgn_form(FL_NO_BOX, 320, 190);
534    obj = fl_add_box(FL_UP_BOX, 0, 0, 320, 190, "");
535    obj = fl_add_box(FL_DOWN_BOX, 10, 50, 300, 110, "");
536    obj = fl_add_button(FL_NORMAL_BUTTON, 250, 10, 50, 30, _("Done"));
537    combo.add(10, 15, 120, 25, 135);
538    fl_end_form();
539
540   return fdui;
541 }
542
543 void combo_cb(int i)
544 {
545    fprintf(stderr, "selected %d:%s\n", i, combo.getline());
546 }
547
548 int main(int argc, char *argv[])
549 {
550         //int n1;
551
552    // Same defaults as in lyx
553    FL_IOPT cntl;
554    cntl.buttonFontSize = FL_NORMAL_SIZE;
555    cntl.browserFontSize = FL_NORMAL_SIZE;
556    cntl.labelFontSize = FL_NORMAL_SIZE;
557    cntl.choiceFontSize = FL_NORMAL_SIZE;
558    cntl.inputFontSize = FL_NORMAL_SIZE;
559    cntl.borderWidth = -2;
560    fl_set_defaults(FL_PDButtonFontSize, &cntl);
561    fl_set_defaults(FL_PDBrowserFontSize, &cntl);
562    fl_set_defaults(FL_PDLabelFontSize, &cntl);
563    fl_set_defaults(FL_PDChoiceFontSize, &cntl);
564    fl_set_defaults(FL_PDInputFontSize, &cntl);
565    fl_set_defaults(FL_PDBorderWidth, &cntl);
566    fl_initialize(&argc, argv, 0, 0, 0);
567
568    fd_test = create_form_test();
569
570    /* fill-in form initialization code */
571    combo.addline("Title");
572    combo.addline("Author");
573    combo.addline("Date");
574    combo.addline("Abstract");
575    combo.addline("Chapter");
576    combo.addline("Section");
577    combo.addline("Subsection");
578    combo.addline("List");
579    combo.addline("Description");
580    combo.addline("Verse");
581    combo.addline("Verbatim");
582    combo.setcallback(combo_cb);
583 //   combo.select(4);
584
585    /* show the first form */
586    fl_show_form(fd_test->test, FL_PLACE_CENTER, FL_FULLBORDER, "test");
587    fl_do_forms();
588    return 0;
589 }
590
591 #endif