]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/FormCitation.C
Merge natbib branch into head
[lyx.git] / src / frontends / xforms / FormCitation.C
1 // -*- C++ -*-
2 /* This file is part of
3  * ====================================================== 
4  *
5  *           LyX, The Document Processor
6  *
7  *           Copyright 2000-2001 The LyX Team.
8  *
9  * ======================================================
10  *
11  * \file FormCitation.C
12  * \author Angus Leeming, a.leeming@ic.ac.uk
13  */
14
15 #include <config.h>
16 #include <algorithm>
17
18 #ifdef __GNUG__
19 #pragma implementation
20 #endif
21
22 #include "xformsBC.h"
23 #include "ControlCitation.h"
24 #include "FormCitation.h"
25 #include "form_citation.h"
26 #include "gettext.h"
27 #include "support/lstrings.h"
28 #include "biblio.h"
29 #include "helper_funcs.h"
30 #include "xforms_helpers.h"
31
32 using std::find;
33 using std::max;
34 using std::min;
35 using std::pair;
36 using std::sort;
37 using std::vector;
38
39 namespace {
40
41 // shamelessly stolen from Menubar_pimpl.C
42 int string_width(string const & str) 
43 {
44         return fl_get_string_widthTAB(FL_NORMAL_STYLE, FL_NORMAL_SIZE,
45                                       str.c_str(),
46                                       static_cast<int>(str.length()));
47 }
48
49
50 void fillChoice(FL_OBJECT * choice, vector<string> const & vec)
51 {
52         string const str = " " + getStringFromVector(vec, " | ") + " ";
53
54         fl_clear_choice(choice);
55         fl_addto_choice(choice, str.c_str());
56
57         int width = 0;
58         for (vector<string>::const_iterator it = vec.begin();
59              it != vec.end(); ++it) {
60                 width = max(width, string_width(*it));
61         }
62
63         // Paranoia checks
64         int const x = max(5, int(choice->x + 0.5 * (choice->w - width)));
65         if (x + width > choice->form->w)
66                 width = choice->form->w - 10;
67         
68         fl_set_object_geometry(choice, x, choice->y, width + 5, choice->h);
69 }
70
71 void updateStyle(FL_OBJECT * choice, FL_OBJECT * full, FL_OBJECT * force,
72                  string command)
73 {
74         // Find the style of the citekeys
75         vector<biblio::CiteStyle> const & styles =
76                 ControlCitation::getCiteStyles();
77         biblio::CitationStyle cs = biblio::getCitationStyle(command);
78
79         vector<biblio::CiteStyle>::const_iterator cit =
80                 find(styles.begin(), styles.end(), cs.style);
81
82         // Use this to initialise the GUI
83         if (cit == styles.end()) {
84                 fl_set_choice(choice, 1);
85                 fl_set_button(full, 0);
86                 fl_set_button(force, 0);
87         } else {
88                 int const i = int(cit - styles.begin());
89                 fl_set_choice(choice, i+1);
90                 fl_set_button(full,  cs.full);
91                 fl_set_button(force, cs.forceUCase);
92         }
93 }
94
95 } // namespace anon
96
97 typedef FormCB<ControlCitation, FormDB<FD_form_citation> > base_class;
98
99 FormCitation::FormCitation(ControlCitation & c)
100         : base_class(c, _("Citation"))
101 {}
102
103
104 void FormCitation::apply()
105 {
106         vector<biblio::CiteStyle> const & styles =
107                 ControlCitation::getCiteStyles();
108
109         int const choice = fl_get_choice(dialog_->choice_style) - 1;
110         bool const full  = fl_get_button(dialog_->button_full_author_list);
111         bool const force = fl_get_button(dialog_->button_force_uppercase);
112
113         string const command =
114                 biblio::getCiteCommand(styles[choice], full, force);
115
116         controller().params().setCmdName(command);
117         controller().params().setContents(getStringFromVector(citekeys));
118
119         string const after  = fl_get_input(dialog_->input_after);
120         controller().params().setOptions(after);
121 }
122
123
124 void FormCitation::hide()
125 {
126         citekeys.clear();
127         bibkeys.clear();
128
129         FormBase::hide();
130 }
131
132
133 void FormCitation::build()
134 {
135         dialog_.reset(build_citation());
136
137         fl_set_input_return(dialog_->input_after,  FL_RETURN_CHANGED);
138         fl_set_input_return(dialog_->input_before, FL_RETURN_CHANGED);
139
140         fl_set_button(dialog_->button_search_case, 0);
141         fl_set_button(dialog_->button_search_type, 0);
142
143         // Manage the ok, apply, restore and cancel/close buttons
144         bc().setOK(dialog_->button_ok);
145         bc().setApply(dialog_->button_apply);
146         bc().setCancel(dialog_->button_cancel);
147         bc().setRestore(dialog_->button_restore);
148
149         bc().addReadOnly(dialog_->button_add);
150         bc().addReadOnly(dialog_->button_del);
151         bc().addReadOnly(dialog_->button_up);
152         bc().addReadOnly(dialog_->button_down);
153         bc().addReadOnly(dialog_->choice_style);
154         bc().addReadOnly(dialog_->input_before);
155         bc().addReadOnly(dialog_->input_after);
156 }
157
158
159 ButtonPolicy::SMInput FormCitation::input(FL_OBJECT * ob, long)
160 {
161         ButtonPolicy::SMInput activate = ButtonPolicy::SMI_NOOP;
162
163         biblio::InfoMap const & theMap = controller().bibkeysInfo();
164
165         string topCitekey;
166         if (!citekeys.empty()) topCitekey = citekeys[0];
167
168         if (ob == dialog_->browser_bib) {
169                 fl_deselect_browser(dialog_->browser_cite);
170
171                 unsigned int const sel = fl_get_browser(dialog_->browser_bib);
172                 if (sel < 1 || sel > bibkeys.size())
173                         return ButtonPolicy::SMI_NOOP;
174
175                 // Put into browser_info the additional info associated with
176                 // the selected browser_bib key
177                 fl_clear_browser(dialog_->browser_info);
178
179                 string const tmp = formatted(biblio::getInfo(theMap,
180                                                              bibkeys[sel-1]),
181                                               dialog_->browser_info->w-10 );
182                 fl_add_browser_line(dialog_->browser_info, tmp.c_str());
183
184                 // Highlight the selected browser_bib key in browser_cite if
185                 // present
186                 vector<string>::const_iterator cit =
187                         find(citekeys.begin(), citekeys.end(), bibkeys[sel-1]);
188
189                 if (cit != citekeys.end()) {
190                         int const n = int(cit - citekeys.begin());
191                         fl_select_browser_line(dialog_->browser_cite, n+1);
192                         fl_set_browser_topline(dialog_->browser_cite, n+1);
193                 }
194
195                 if (!controller().isReadonly()) {
196                         if (cit != citekeys.end()) {
197                                 setBibButtons(OFF);
198                                 setCiteButtons(ON);
199                         } else {
200                                 setBibButtons(ON);
201                                 setCiteButtons(OFF);
202                         }
203                 }
204
205         } else if (ob == dialog_->browser_cite) {
206                 unsigned int const sel = fl_get_browser(dialog_->browser_cite);
207                 if (sel < 1 || sel > citekeys.size())
208                         return ButtonPolicy::SMI_NOOP;
209
210                 if (!controller().isReadonly()) {
211                         setBibButtons(OFF);
212                         setCiteButtons(ON);
213                 }
214
215                 // Highlight the selected browser_cite key in browser_bib
216                 vector<string>::const_iterator cit =
217                         find(bibkeys.begin(), bibkeys.end(), citekeys[sel-1]);
218
219                 if (cit != bibkeys.end()) {
220                         int const n = int(cit - bibkeys.begin());
221                         fl_select_browser_line(dialog_->browser_bib, n+1);
222                         fl_set_browser_topline(dialog_->browser_bib, n+1);
223
224                         // Put into browser_info the additional info associated
225                         // with the selected browser_cite key
226                         fl_clear_browser(dialog_->browser_info);
227                         string const tmp =
228                                 formatted(biblio::getInfo(theMap,
229                                                           citekeys[sel-1]),
230                                           dialog_->browser_info->w-10);
231                         fl_add_browser_line(dialog_->browser_info, tmp.c_str());
232                 }
233
234         } else if (ob == dialog_->button_add) {
235                 unsigned int const sel = fl_get_browser(dialog_->browser_bib);
236                 if (sel < 1 || sel > bibkeys.size())
237                         return ButtonPolicy::SMI_NOOP;
238
239                 // Add the selected browser_bib key to browser_cite
240                 fl_addto_browser(dialog_->browser_cite,
241                                   bibkeys[sel-1].c_str());
242                 citekeys.push_back(bibkeys[sel-1]);
243
244                 int const n = int(citekeys.size());
245                 fl_select_browser_line(dialog_->browser_cite, n);
246
247                 setBibButtons(OFF);
248                 setCiteButtons(ON);
249                 activate = ButtonPolicy::SMI_VALID;
250
251         } else if (ob == dialog_->button_del) {
252                 unsigned int const sel = fl_get_browser(dialog_->browser_cite);
253                 if (sel < 1 || sel > citekeys.size())
254                         return ButtonPolicy::SMI_NOOP;
255
256                 // Remove the selected key from browser_cite
257                 fl_delete_browser_line(dialog_->browser_cite, sel) ;
258                 citekeys.erase(citekeys.begin() + sel-1);
259
260                 setBibButtons(ON);
261                 setCiteButtons(OFF);
262                 activate = ButtonPolicy::SMI_VALID;
263
264         } else if (ob == dialog_->button_up) {
265                 unsigned int const sel = fl_get_browser(dialog_->browser_cite);
266                 if (sel < 2 || sel > citekeys.size())
267                         return ButtonPolicy::SMI_NOOP;
268
269                 // Move the selected key up one line
270                 vector<string>::iterator it = citekeys.begin() + sel-1;
271                 string const tmp = *it;
272
273                 fl_delete_browser_line(dialog_->browser_cite, sel);
274                 citekeys.erase(it);
275
276                 fl_insert_browser_line(dialog_->browser_cite, sel-1, tmp.c_str());
277                 fl_select_browser_line(dialog_->browser_cite, sel-1);
278                 citekeys.insert(it-1, tmp);
279                 setCiteButtons(ON);
280                 activate = ButtonPolicy::SMI_VALID;
281
282         } else if (ob == dialog_->button_down) {
283                 unsigned int const sel = fl_get_browser(dialog_->browser_cite);
284                 if (sel < 1 || sel > citekeys.size()-1)
285                         return ButtonPolicy::SMI_NOOP;
286
287                 // Move the selected key down one line
288                 vector<string>::iterator it = citekeys.begin() + sel-1;
289                 string const tmp = *it;
290
291                 fl_delete_browser_line(dialog_->browser_cite, sel);
292                 citekeys.erase(it);
293
294                 fl_insert_browser_line(dialog_->browser_cite, sel+1, tmp.c_str());
295                 fl_select_browser_line(dialog_->browser_cite, sel+1);
296                 citekeys.insert(it+1, tmp);
297                 setCiteButtons(ON);
298                 activate = ButtonPolicy::SMI_VALID;
299
300         } else if (ob == dialog_->button_previous ||
301                    ob == dialog_->button_next) {
302
303                 string const str = fl_get_input(dialog_->input_search);
304
305                 biblio::Direction const dir =
306                         (ob == dialog_->button_previous) ?
307                         biblio::BACKWARD : biblio::FORWARD;
308
309                 biblio::Search const type =
310                         fl_get_button(dialog_->button_search_type) ?
311                         biblio::REGEX : biblio::SIMPLE;
312
313                 vector<string>::const_iterator start = bibkeys.begin();
314                 int const sel = fl_get_browser(dialog_->browser_bib);
315                 if (sel >= 1 && sel <= int(bibkeys.size()))
316                         start += sel-1;
317
318                 // Find the NEXT instance...
319                 if (dir == biblio::FORWARD)
320                         start += 1;
321                 else
322                         start -= 1;
323
324                 bool const caseSensitive =
325                         fl_get_button(dialog_->button_search_case);
326
327                 vector<string>::const_iterator const cit =
328                         biblio::searchKeys(theMap, bibkeys, str,
329                                            start, type, dir, caseSensitive);
330
331                 if (cit == bibkeys.end())
332                         return ButtonPolicy::SMI_NOOP;
333
334                 int const found = int(cit - bibkeys.begin()) + 1;
335                 if (found == sel)
336                         return ButtonPolicy::SMI_NOOP;
337
338                 // Update the display
339                 int const top = max(found-5, 1);
340                 fl_set_browser_topline(dialog_->browser_bib, top);
341                 fl_select_browser_line(dialog_->browser_bib, found);
342                 input(dialog_->browser_bib, 0);
343
344         } else if (ob == dialog_->choice_style ||
345                    ob == dialog_->button_full_author_list ||
346                    ob == dialog_->button_force_uppercase ||
347                    ob == dialog_->input_before ||
348                    ob == dialog_->input_after) {
349                 activate = ButtonPolicy::SMI_VALID;
350         }
351
352         string currentCitekey;
353         if (!citekeys.empty())
354                 currentCitekey = citekeys[0];
355
356         if (topCitekey != currentCitekey) {
357                 int choice = fl_get_choice(dialog_->choice_style);
358                 fillChoice(dialog_->choice_style,
359                            controller().getCiteStrings(currentCitekey));
360                 fl_set_choice(dialog_->choice_style, choice);
361         }
362
363         return activate;
364 }
365
366
367 void FormCitation::update()
368 {
369         // Make the list of all available bibliography keys
370         bibkeys = biblio::getKeys(controller().bibkeysInfo());
371         updateBrowser(dialog_->browser_bib, bibkeys);
372
373         // Ditto for the keys cited in this inset
374         citekeys = getVectorFromString(controller().params().getContents());
375         updateBrowser(dialog_->browser_cite, citekeys);
376
377         // Use the first citekey to fill choice_style
378         string key;
379         if (!citekeys.empty()) key = citekeys[0];
380
381         fillChoice(dialog_->choice_style, controller().getCiteStrings(key));
382
383         // Use the citation command to update the GUI
384         updateStyle(dialog_->choice_style, 
385                     dialog_->button_full_author_list,
386                     dialog_->button_force_uppercase,
387                     controller().params().getCmdName());
388         
389         // No keys have been selected yet, so...
390         fl_clear_browser(dialog_->browser_info);
391         setBibButtons(OFF);
392         setCiteButtons(OFF);
393
394         // Natbib can have comments before and after the citation.
395         // This is not yet supported. After only.
396         fl_set_input(dialog_->input_after,
397                      controller().params().getOptions().c_str());
398
399         fl_set_input(dialog_->input_before, _("Not yet supported"));
400         setEnabled(dialog_->input_before, false);
401 }
402
403
404 void FormCitation::updateBrowser(FL_OBJECT * browser,
405                                  vector<string> const & keys) const
406 {
407         fl_clear_browser(browser);
408
409         for (vector<string>::const_iterator it = keys.begin();
410              it < keys.end(); ++it) {
411                 string key = frontStrip(strip(*it));
412                 if (!key.empty())
413                         fl_add_browser_line(browser, key.c_str());
414         }
415 }
416
417
418 void FormCitation::setBibButtons(State status) const
419 {
420         setEnabled(dialog_->button_add, (status == ON));
421 }
422
423
424 void FormCitation::setCiteButtons(State status) const
425 {
426         int const sel     = fl_get_browser(dialog_->browser_cite);
427         int const maxline = fl_get_browser_maxline(dialog_->browser_cite);
428         bool const activate      = (status == ON);
429         bool const activate_up   = (activate && sel != 1);
430         bool const activate_down = (activate && sel != maxline);
431
432         setEnabled(dialog_->button_del,  activate);
433         setEnabled(dialog_->button_up,   activate_up);
434         setEnabled(dialog_->button_down, activate_down);
435 }