]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/combox.c
b13a2c73eabe814a738714712c2def8823911ea3
[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 FORMS_H_LOCATION
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     if (!ob || ob->objclass != FL_COMBOX)
371         return 0;
372
373     switch (event) {
374     case FL_DRAW:
375         attrib_change(ob->spec);
376         /* Fall through */
377     case FL_DRAWLABEL:
378         fl_draw_object_label(ob);
379         break;
380     case FL_SHORTCUT:
381         show_browser(ob->spec);
382         break;
383     case FL_FREEMEM: {
384         COMBOX_SPEC * sp = ob->spec;
385         fl_free_freebrowser(sp->freebrowser);
386         /* children take care of themselves, but we must make sure that
387            sp itself is free-d eventually. */
388         fl_addto_freelist(sp);
389         break;
390     }
391     }
392     return 0;
393 }
394
395
396 static void
397 set_activation(FL_OBJECT * ob, int activation)
398 {
399     switch (activation) {
400     case ACTIVATE:
401         fl_activate_object(ob);
402         fl_set_object_lcol(ob, FL_LCOL);
403         break;
404     case DEACTIVATE:
405         fl_deactivate_object(ob);
406         fl_set_object_lcol(ob, FL_INACTIVE);
407     }
408 }
409
410
411 static void
412 show_browser(COMBOX_SPEC * sp)
413 {
414     FL_OBJECT * ob = sp->combox;
415
416     /* The browser dimensions. */
417     FL_Coord const bw = ob->w + 20;
418     FL_Coord const bh = sp->browser_height;
419
420     FL_Coord const abs_x = ob->form->x + ob->x;
421
422     FL_Coord abs_y = ob->form->y + ob->y;
423     abs_y += (sp->browser_position == FL_FREEBROWSER_BELOW) ? ob->h : -bh;
424
425     set_state_label(sp, COMBOX_OPEN);
426     fl_show_freebrowser(sp->freebrowser, abs_x, abs_y, bw, bh);
427 }
428
429
430 static void
431 state_cb(FL_OBJECT * ob, long data)
432 {
433     show_browser(ob->u_vdata);
434 }
435
436
437 static void
438 chosen_cb(FL_OBJECT * ob, long data)
439 {
440     show_browser(ob->u_vdata);
441 }
442
443
444 static void
445 update_button_chosen(FL_FREEBROWSER * fb, int action)
446 {
447     COMBOX_SPEC * sp = fb->parent;
448
449     FL_OBJECT * browser = sp->freebrowser->browser;
450     FL_OBJECT * combox = sp->combox;
451     if (!browser || !combox) return;
452
453     set_state_label(sp, COMBOX_CLOSED);
454
455     if (action == 1) {
456         int const sel = fl_get_browser(browser);
457         char const * const text = fl_get_browser_line(browser, sel);
458         fl_set_object_label(sp->button_chosen, text);
459         fl_call_object_callback(combox);
460     }
461 }
462
463
464 static void
465 set_state_label(COMBOX_SPEC * sp, int state)
466 {
467     char const * const up   = "@2<-";
468     char const * const down = "@2->";
469     char const * label = 0;
470
471     if (!sp->button_state)
472         return;
473
474     if (sp->browser_position == FL_FREEBROWSER_BELOW) {
475         label = (state == COMBOX_OPEN) ? up : down;
476     } else {
477         label = (state == COMBOX_OPEN) ? down : up;
478     }
479     fl_set_object_label(sp->button_state, label);
480     fl_redraw_object(sp->button_state);
481 }
482
483
484 static void
485 attrib_change(COMBOX_SPEC * sp)
486 {
487     FL_OBJECT * parent = sp->combox;
488     FL_OBJECT * button = sp->button_chosen;
489
490     button->boxtype = parent->boxtype;
491     button->col1    = parent->col1;
492     button->col2    = parent->col2;
493     button->bw      = parent->bw;
494
495     if (sp->button_state) {
496         button = sp->button_state;
497
498         /* The boxtype is not changed */
499         button->col1    = parent->col1;
500         button->col2    = parent->col2;
501         button->bw      = parent->bw;
502     }
503 }