]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/combox.c
ditch FileInfo -> use boost.filesystem
[lyx.git] / src / frontends / xforms / combox.c
1 /**
2  * \file combox.c
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author Angus Leeming
10  *
11  * Full author contact details are available in file CREDITS
12  *
13  * This is a rewrite of Alejandro's C++ Combox class, originally written
14  * for LyX in 1996. The rewrite turns it into a native xforms widget.
15  */
16 #include <config.h>
17
18 #include "lyx_forms.h"
19 #include "combox.h"
20 #include "freebrowser.h"
21
22 extern void fl_add_child(FL_OBJECT *, FL_OBJECT *);
23 extern void fl_addto_freelist(void *);
24
25
26 typedef struct {
27      /** A pointer to the parent widget */
28     FL_OBJECT * combox;
29
30     FL_FREEBROWSER * freebrowser;
31     int browser_height;
32
33     /** The browser will be displayed either below or above the main body. */
34     int browser_position;
35
36     /**  button_state displays a down or up arrow depending on whether the
37      *   browser is visible or not.
38      *   Click on it to toggle the browser.
39      */
40     FL_OBJECT * button_state;
41
42     /**  button_chosen displays the current selection from the browser.
43      *   Click on it to toggle the browser.
44      */
45     FL_OBJECT * button_chosen;
46 } COMBOX_SPEC;
47
48
49 enum { ACTIVATE, DEACTIVATE };
50
51 enum { COMBOX_OPEN, COMBOX_CLOSED };
52
53
54 /* function declarations */
55 static int combox_pre(FL_OBJECT *, int, FL_Coord, FL_Coord, int, void *);
56 static int combox_post(FL_OBJECT *, int, FL_Coord, FL_Coord, int, void *);
57 static int combox_handle(FL_OBJECT *, int, FL_Coord, FL_Coord, int, void *);
58
59 static void update_button_chosen(FL_FREEBROWSER * fb, int action);
60 static void chosen_cb(FL_OBJECT * ob, long data);
61 static void state_cb(FL_OBJECT * ob, long data);
62
63 static void show_browser(COMBOX_SPEC * sp);
64 static void set_activation(FL_OBJECT * ob, int activation);
65 static void set_state_label(COMBOX_SPEC * sp, int state);
66 static void attrib_change(COMBOX_SPEC * sp);
67
68
69 FL_OBJECT *
70 fl_create_combox(FL_COMBOX_TYPE type,
71                  FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h,
72                  char const * label)
73 {
74     FL_OBJECT * ob;
75     FL_OBJECT * button;
76     COMBOX_SPEC * sp;
77
78     /* The width and x-position of button_state, respectively. */
79     FL_Coord const ws = 0.7 * h;
80     FL_Coord const xs = x + w - ws;
81
82     /* The width of button_chosen */
83     FL_Coord const wc = (type == FL_DROPLIST_COMBOX) ? (w - ws) : w;
84
85     ob = fl_make_object(FL_COMBOX, type, x, y, w, h, label, combox_handle);
86     ob->align = FL_ALIGN_LEFT;
87
88     sp = ob->spec = fl_calloc(1, sizeof(COMBOX_SPEC));
89     sp->combox = ob;
90     sp->browser_height = 100;
91     sp->browser_position = FL_FREEBROWSER_BELOW;
92
93     sp->freebrowser = fl_create_freebrowser(sp);
94     sp->freebrowser->callback = update_button_chosen;
95
96     sp->button_state = 0;
97     if (type == FL_DROPLIST_COMBOX) {
98         sp->button_state = fl_add_button(FL_NORMAL_BUTTON, xs, y, ws, h, "");
99
100         button = sp->button_state;
101         fl_set_object_lalign(button, FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
102         fl_set_object_callback(button, state_cb, 0);
103         fl_set_object_posthandler(button, combox_post);
104         fl_set_object_prehandler(button,  combox_pre);
105         set_state_label(sp, COMBOX_CLOSED);
106
107         set_activation(button, DEACTIVATE);
108         button->parent = ob;
109         button->u_vdata = sp;
110     }
111
112     sp->button_chosen = fl_add_button(FL_NORMAL_TEXT, x, y, wc, h, "");
113
114     button = sp->button_chosen;
115     fl_set_object_boxtype(button, FL_FRAME_BOX);
116     fl_set_object_lalign(button, FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
117     fl_set_object_callback(button, chosen_cb, 0);
118     fl_set_object_posthandler(button, combox_post);
119     fl_set_object_prehandler(button,  combox_pre);
120     set_activation(button, DEACTIVATE);
121     button->parent = ob;
122     button->u_vdata = sp;
123
124     return ob;
125 }
126
127
128 FL_OBJECT *
129 fl_add_combox(FL_COMBOX_TYPE type,
130               FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h,
131               char const * label)
132 {
133     FL_OBJECT * ob = fl_create_combox(type, x, y, w, h, label);
134     COMBOX_SPEC * sp = ob->spec;
135
136     if (sp->button_state)
137         fl_add_child(ob, sp->button_state);
138     fl_add_child(ob, sp->button_chosen);
139
140     fl_add_object(fl_current_form, ob);
141     return ob;
142 }
143
144
145 void
146 fl_set_combox_browser_height(FL_OBJECT * ob, int bh)
147 {
148     COMBOX_SPEC * sp;
149
150     if (!ob || ob->objclass != FL_COMBOX)
151         return;
152
153     sp = ob->spec;
154     sp->browser_height = bh;
155 }
156
157
158 void
159 fl_set_combox_position(FL_OBJECT * ob, FL_COMBOX_POSITION position)
160 {
161     COMBOX_SPEC * sp;
162
163     if (!ob || ob->objclass != FL_COMBOX)
164         return;
165
166     sp = ob->spec;
167     sp->browser_position = (position == FL_COMBOX_ABOVE) ?
168         FL_FREEBROWSER_ABOVE : FL_FREEBROWSER_BELOW;
169
170     set_state_label(sp, COMBOX_CLOSED);
171 }
172
173
174 void
175 fl_clear_combox(FL_OBJECT * ob)
176 {
177     COMBOX_SPEC * sp;
178     FL_OBJECT * browser;
179
180     if (!ob || ob->objclass != FL_COMBOX)
181         return;
182
183     sp = ob->spec;
184     browser = sp->freebrowser->browser;
185
186     fl_clear_browser(browser);
187     fl_set_object_label(sp->button_chosen, "");
188 }
189
190
191 void
192 fl_addto_combox(FL_OBJECT * ob, char const * text)
193 {
194     COMBOX_SPEC * sp;
195     FL_OBJECT * browser;
196     int i;
197     int j;
198     char line[128];
199
200     if (!ob || ob->objclass != FL_COMBOX)
201         return;
202
203     sp = ob->spec;
204     browser = sp->freebrowser->browser;
205
206     /* Split the string on '|' boundaries. */
207     i = j = 0;
208     for (; text[i] != '\0'; ++i) {
209         if (text[i] == '|') {
210             line[j] = '\0';
211             fl_add_browser_line(browser, line);
212             j = 0;
213         } else {
214             line[j++] = text[i];
215         }
216     }
217
218     if (j != 0) {
219             line[j] = '\0';
220             fl_add_browser_line(browser, line);
221     }
222
223     /* By default the first item is selected */
224     if (fl_get_browser_maxline(browser)) {
225         char const * const label = fl_get_browser_line(browser, 1);
226         fl_set_object_label(sp->button_chosen, label);
227         fl_select_browser_line(browser, 1);
228         set_activation(sp->button_chosen, ACTIVATE);
229         if (sp->button_state)
230             set_activation(sp->button_state,  ACTIVATE);
231     }
232 }
233
234
235 void
236 fl_set_combox(FL_OBJECT * ob, int sel)
237 {
238     COMBOX_SPEC * sp;
239     FL_OBJECT * browser;
240
241     if (!ob || ob->objclass != FL_COMBOX)
242         return;
243
244     sp = ob->spec;
245     browser = sp->freebrowser->browser;
246
247     if (sel < 1 || sel > fl_get_browser_maxline(browser))
248         return;
249
250     fl_select_browser_line(browser, sel);
251     fl_set_object_label(sp->button_chosen, fl_get_browser_line(browser, sel));
252 }
253
254
255 int
256 fl_get_combox(FL_OBJECT * ob)
257 {
258     COMBOX_SPEC * sp;
259     FL_OBJECT * browser;
260
261     if (!ob || ob->objclass != FL_COMBOX)
262         return 0;
263
264     sp = ob->spec;
265     browser = sp->freebrowser->browser;
266     return fl_get_browser(browser);
267 }
268
269
270 char const *
271 fl_get_combox_text(FL_OBJECT * ob)
272 {
273     COMBOX_SPEC * sp;
274
275     if (!ob || ob->objclass != FL_COMBOX)
276         return 0;
277
278     sp = ob->spec;
279     return sp->button_chosen->label;
280 }
281
282
283 char const *
284 fl_get_combox_line(FL_OBJECT * ob, int line)
285 {
286     COMBOX_SPEC * sp;
287     FL_OBJECT * browser;
288     int maxlines;
289
290     if (line < 1 || !ob || ob->objclass != FL_COMBOX)
291         return 0;
292
293     sp = ob->spec;
294     browser = sp->freebrowser->browser;
295
296     maxlines = fl_get_browser_maxline(browser);
297     if (line > maxlines)
298         return 0;
299
300     return fl_get_browser_line(browser, line);
301 }
302
303
304 int
305 fl_get_combox_maxitems(FL_OBJECT * ob)
306 {
307     COMBOX_SPEC * sp;
308     FL_OBJECT * browser;
309
310     if (!ob || ob->objclass != FL_COMBOX)
311         return 0;
312
313     sp = ob->spec;
314     browser = sp->freebrowser->browser;
315     return fl_get_browser_maxline(browser);
316 }
317
318
319 void
320 fl_show_combox_browser(FL_OBJECT * ob)
321 {
322     if (!ob || ob->objclass != FL_COMBOX)
323         return;
324
325     show_browser(ob->spec);
326 }
327
328
329 void
330 fl_hide_combox_browser(FL_OBJECT * ob)
331 {
332     COMBOX_SPEC * sp;
333
334     if (!ob || ob->objclass != FL_COMBOX)
335         return;
336
337     sp = ob->spec;
338     fl_free_freebrowser(sp->freebrowser);
339 }
340
341
342 static int
343 combox_pre(FL_OBJECT * ob, int ev, FL_Coord mx, FL_Coord my, int key,
344            void * xev)
345 {
346     COMBOX_SPEC * sp = ob->u_vdata;
347     FL_OBJECT * combox = sp->combox;
348
349     return combox->prehandle ?
350         combox->prehandle(combox, ev, mx, my, key, xev) : 0;
351 }
352
353
354 static int
355 combox_post(FL_OBJECT * ob, int ev, FL_Coord mx, FL_Coord my, int key,
356             void * xev)
357 {
358     COMBOX_SPEC * sp = ob->u_vdata;
359     FL_OBJECT * combox = sp->combox;
360
361     return combox->posthandle ?
362         combox->posthandle(combox, ev, mx, my, key, xev) : 0;
363 }
364
365
366 static int
367 combox_handle(FL_OBJECT * ob, int event, FL_Coord mx, FL_Coord my, int key,
368               void * ev)
369 {
370     /* Silence warnings about unused parameters */
371     (void) mx;
372     (void) my;
373     (void) key;
374     (void) ev;
375
376     if (!ob || ob->objclass != FL_COMBOX)
377         return 0;
378
379     switch (event) {
380     case FL_DRAW:
381         attrib_change(ob->spec);
382         /* Fall through */
383     case FL_DRAWLABEL: {
384         COMBOX_SPEC * sp = ob->spec;
385
386         int change =
387             ob->x != sp->button_chosen->x ||
388             ob->y != sp->button_chosen->y;
389
390         FL_Coord xbs = 0;
391         if (sp->button_state) {
392             xbs = ob->x + (sp->button_state->x - sp->button_chosen->x);
393             change = change ||
394                 xbs   != sp->button_state->x ||
395                 ob->y != sp->button_state->y;
396         }
397
398         if (change) {
399             fl_freeze_form(ob->form);
400             fl_set_object_position(sp->button_chosen, ob->x, ob->y);
401             if (sp->button_state)
402                 fl_set_object_position(sp->button_state, xbs, ob->y);
403             fl_unfreeze_form(ob->form);
404         }
405
406         fl_draw_object_label(ob);
407         break;
408     } case FL_SHORTCUT:
409         show_browser(ob->spec);
410         break;
411     case FL_FREEMEM: {
412         COMBOX_SPEC * sp = ob->spec;
413         fl_free_freebrowser(sp->freebrowser);
414         /* children take care of themselves, but we must make sure that
415            sp itself is free-d eventually. */
416         fl_addto_freelist(sp);
417         break;
418     }
419     }
420     return 0;
421 }
422
423
424 static void
425 set_activation(FL_OBJECT * ob, int activation)
426 {
427     switch (activation) {
428     case ACTIVATE:
429         fl_activate_object(ob);
430         fl_set_object_lcol(ob, FL_LCOL);
431         break;
432     case DEACTIVATE:
433         fl_deactivate_object(ob);
434         fl_set_object_lcol(ob, FL_INACTIVE);
435     }
436 }
437
438
439 static void
440 show_browser(COMBOX_SPEC * sp)
441 {
442     FL_OBJECT * ob = sp->combox;
443
444     /* The browser dimensions. */
445     FL_Coord const bw = ob->w + 20;
446     FL_Coord const bh = sp->browser_height;
447
448     FL_Coord const abs_x = ob->form->x + ob->x;
449
450     FL_Coord abs_y = ob->form->y + ob->y;
451     abs_y += (sp->browser_position == FL_FREEBROWSER_BELOW) ? ob->h : -bh;
452
453     set_state_label(sp, COMBOX_OPEN);
454     fl_show_freebrowser(sp->freebrowser, abs_x, abs_y, bw, bh);
455 }
456
457
458 static void
459 state_cb(FL_OBJECT * ob, long data)
460 {
461     /* Silence warning about unused parameter */
462     (void) data;
463
464     show_browser(ob->u_vdata);
465 }
466
467
468 static void
469 chosen_cb(FL_OBJECT * ob, long data)
470 {
471     /* Silence warning about unused parameter */
472     (void) data;
473
474     show_browser(ob->u_vdata);
475 }
476
477
478 static void
479 update_button_chosen(FL_FREEBROWSER * fb, int action)
480 {
481     COMBOX_SPEC * sp = fb->parent;
482
483     FL_OBJECT * browser = sp->freebrowser->browser;
484     FL_OBJECT * combox = sp->combox;
485     if (!browser || !combox) return;
486
487     set_state_label(sp, COMBOX_CLOSED);
488
489     if (action == 1) {
490         int const sel = fl_get_browser(browser);
491         char const * const text = fl_get_browser_line(browser, sel);
492         fl_set_object_label(sp->button_chosen, text);
493         fl_call_object_callback(combox);
494     }
495 }
496
497
498 static void
499 set_state_label(COMBOX_SPEC * sp, int state)
500 {
501     char const * const up   = "@2<-";
502     char const * const down = "@2->";
503     char const * label = 0;
504
505     if (!sp->button_state)
506         return;
507
508     if (sp->browser_position == FL_FREEBROWSER_BELOW) {
509         label = (state == COMBOX_OPEN) ? up : down;
510     } else {
511         label = (state == COMBOX_OPEN) ? down : up;
512     }
513     fl_set_object_label(sp->button_state, label);
514     fl_redraw_object(sp->button_state);
515 }
516
517
518 static void
519 attrib_change(COMBOX_SPEC * sp)
520 {
521     FL_OBJECT * parent = sp->combox;
522     FL_OBJECT * button = sp->button_chosen;
523
524     button->boxtype = parent->boxtype;
525     button->col1    = parent->col1;
526     button->col2    = parent->col2;
527     button->bw      = parent->bw;
528
529     if (sp->button_state) {
530         button = sp->button_state;
531
532         /* The boxtype is not changed */
533         button->col1    = parent->col1;
534         button->col2    = parent->col2;
535         button->bw      = parent->bw;
536     }
537 }