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