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