]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/FormCitation.C
This file is part of LyX, the document processor.
[lyx.git] / src / frontends / xforms / FormCitation.C
1 /**
2  * \file xforms/FormCitation.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Angus Leeming 
7  *
8  * Full author contact details are available in file CREDITS
9  */
10
11 #include <config.h>
12 #include <algorithm>
13
14 #ifdef __GNUG__
15 #pragma implementation
16 #endif
17
18 #include "xformsBC.h"
19 #include "ControlCitation.h"
20 #include "FormCitation.h"
21 #include "forms/form_citation.h"
22 #include "Tooltips.h"
23 #include "helper_funcs.h"
24 #include "xforms_helpers.h"
25 #include FORMS_H_LOCATION
26
27 #include "gettext.h"
28 #include "support/LAssert.h"
29 #include "support/lstrings.h"
30
31 using std::find;
32 using std::max;
33 using std::min;
34 using std::pair;
35 using std::sort;
36 using std::vector;
37
38 namespace {
39
40 // shamelessly stolen from Menubar_pimpl.C
41 int string_width(string const & str)
42 {
43         return fl_get_string_widthTAB(FL_NORMAL_STYLE, FL_NORMAL_SIZE,
44                                       str.c_str(),
45                                       static_cast<int>(str.length()));
46 }
47
48
49 void fillChoice(FD_citation * dialog, vector<string> vec)
50 {
51         // Check whether the current contents of the browser will be
52         // changed by loading the contents of the vec...
53         vector<string> const choice_style =
54                 getVector(dialog->choice_style);
55
56         if (vec == choice_style)
57                 return;
58
59         // They will be changed. Proceed
60         string str = " ";
61         if (!vec.empty())
62                 str += getStringFromVector(vec, " | ") + " ";
63
64         fl_clear_choice(dialog->choice_style);
65         fl_addto_choice(dialog->choice_style, str.c_str());
66
67         setEnabled(dialog->choice_style, !vec.empty());
68         if (vec.empty())
69                 return;
70
71         // The width of the choice varies with the contents.
72         // Ensure that it is centred in the frame.
73
74         int width = 0;
75         for (vector<string>::const_iterator it = vec.begin();
76              it != vec.end(); ++it) {
77                 width = max(width, string_width(*it));
78         }
79
80         int const dx =
81                 max(5, int(0.5 * (dialog->frame_style->w - width)));
82
83         fl_set_object_geometry(dialog->choice_style,
84                                dialog->frame_style->x + dx,
85                                dialog->choice_style->y,
86                                width,
87                                dialog->choice_style->h);
88 }
89
90
91 void updateStyle(FD_citation * dialog, string command)
92 {
93         // Find the style of the citekeys
94         vector<biblio::CiteStyle> const & styles =
95                 ControlCitation::getCiteStyles();
96         biblio::CitationStyle cs = biblio::getCitationStyle(command);
97
98         vector<biblio::CiteStyle>::const_iterator cit =
99                 find(styles.begin(), styles.end(), cs.style);
100
101         // Use this to initialise the GUI
102         if (cit == styles.end()) {
103                 fl_set_choice(dialog->choice_style, 1);
104                 fl_set_button(dialog->check_full_author_list, 0);
105                 fl_set_button(dialog->check_force_uppercase, 0);
106         } else {
107                 int const i = int(cit - styles.begin());
108                 fl_set_choice(dialog->choice_style, i+1);
109                 fl_set_button(dialog->check_full_author_list,  cs.full);
110                 fl_set_button(dialog->check_force_uppercase, cs.forceUCase);
111         }
112 }
113
114 } // namespace anon
115
116
117 typedef FormCB<ControlCitation, FormDB<FD_citation> > base_class;
118
119
120 FormCitation::FormCitation()
121         : base_class(_("Citation"), false)
122 {}
123
124
125 void FormCitation::apply()
126 {
127         string command = "cite";
128         if (dialog_->choice_style->active != 0) {
129                 vector<biblio::CiteStyle> const & styles =
130                         ControlCitation::getCiteStyles();
131
132                 int const choice =
133                         std::max(0, fl_get_choice(dialog_->choice_style) - 1);
134                 bool const full  =
135                         fl_get_button(dialog_->check_full_author_list);
136                 bool const force =
137                         fl_get_button(dialog_->check_force_uppercase);
138
139                 command = biblio::getCiteCommand(styles[choice], full, force);
140         }
141
142         controller().params().setCmdName(command);
143         controller().params().setContents(getStringFromVector(citekeys));
144
145         string const after  = fl_get_input(dialog_->input_after);
146         controller().params().setOptions(after);
147 }
148
149
150 void FormCitation::hide()
151 {
152         citekeys.clear();
153         bibkeys.clear();
154
155         FormBase::hide();
156 }
157
158
159 void FormCitation::build()
160 {
161         dialog_.reset(build_citation(this));
162
163         fl_set_input_return(dialog_->input_after,  FL_RETURN_CHANGED);
164         fl_set_input_return(dialog_->input_before, FL_RETURN_CHANGED);
165         fl_set_input_return(dialog_->input_search, FL_RETURN_END);
166
167         fl_set_button(dialog_->check_search_case, 0);
168         fl_set_button(dialog_->check_search_type, 0);
169
170         setPrehandler(dialog_->input_search);
171         setPrehandler(dialog_->input_before);
172         setPrehandler(dialog_->input_after);
173
174         // Manage the ok, apply, restore and cancel/close buttons
175         bc().setOK(dialog_->button_ok);
176         bc().setApply(dialog_->button_apply);
177         bc().setCancel(dialog_->button_close);
178         bc().setRestore(dialog_->button_restore);
179
180         bc().addReadOnly(dialog_->button_add);
181         bc().addReadOnly(dialog_->button_del);
182         bc().addReadOnly(dialog_->button_up);
183         bc().addReadOnly(dialog_->button_down);
184         bc().addReadOnly(dialog_->choice_style);
185         bc().addReadOnly(dialog_->input_before);
186         bc().addReadOnly(dialog_->input_after);
187         bc().addReadOnly(dialog_->check_full_author_list);
188         bc().addReadOnly(dialog_->check_force_uppercase);
189
190         //set up the tooltip mechanism
191         string str = _("Add the selected entry to the current citation reference.");
192         tooltips().init(dialog_->button_add, str);
193
194         str = _("Delete the selected entry from the current citation reference.");
195         tooltips().init(dialog_->button_del, str);
196
197         str = _("Move the selected entry upwards (in the current list).");
198         tooltips().init(dialog_->button_up, str);
199
200         str = _("Move the selected entry downwards (in the current list).");
201         tooltips().init(dialog_->button_down, str);
202
203         str = _("The entries which will be cited. Select them with the arrow buttons from the right browser window.");
204         tooltips().init(dialog_->browser_cite, str);
205
206         str = _("All entries in the database you have loaded (via \"Insert->Lists&TOC->BibTex Reference\"). Move the ones you want to cite with the arrow buttons into the left browser window.");
207         tooltips().init(dialog_->browser_bib, str);
208
209         str = _("Information about the selected entry");
210         tooltips().init(dialog_->browser_info, str);
211
212         str = _("Here you may select how the citation label should look inside the text (Natbib).");
213         tooltips().init(dialog_->choice_style, str);
214
215         str = _("Activate if you want to print all authors in a reference with more than three authors, and not \"<First Author> et al.\" (Natbib).");
216         tooltips().init(dialog_->check_full_author_list, str);
217
218         str = _("Activate if you want to print the first character of the author name as uppercase (\"Van Gogh\", not \"van Gogh\"). Useful at the beginning of sentences (Natbib).");
219         tooltips().init(dialog_->check_force_uppercase, str);
220
221         str = _("Optional text which appears before the citation reference, e.g. \"see <Ref>\"");
222         tooltips().init(dialog_->input_before, str);
223
224         str = _("Optional text which appears after the citation reference, e.g. \"pp. 12\"");
225         tooltips().init(dialog_->input_after, str);
226
227         str = _("Search your database (all fields will be searched).");
228         tooltips().init(dialog_->input_search, str);
229
230         str = _("Activate if you want to have case sensitive search: \"bibtex\" finds \"bibtex\", but not \"BibTeX\".");
231         tooltips().init(dialog_->check_search_case, str);
232
233         str = _("Activate if you want to enter Regular Expressions.");
234         tooltips().init(dialog_->check_search_type, str);
235 }
236
237
238 void FormCitation::findBiblio(biblio::Direction const dir)
239 {
240         string const str = fl_get_input(dialog_->input_search);
241         biblio::InfoMap const & theMap = controller().bibkeysInfo();
242         bool const caseSensitive =
243                 fl_get_button(dialog_->check_search_case);
244         biblio::Search const type =
245                 fl_get_button(dialog_->check_search_type) ?
246                 biblio::REGEX : biblio::SIMPLE;
247
248         vector<string>::const_iterator start = bibkeys.begin();
249         int const sel = fl_get_browser(dialog_->browser_bib);
250         if (sel >= 1 && sel <= int(bibkeys.size()))
251                 start += sel - 1;
252
253         // Find the NEXT instance...
254         (dir == biblio::FORWARD) ? ++start : --start;
255
256
257         vector<string>::const_iterator const cit =
258                 biblio::searchKeys(theMap, bibkeys, str,
259                                    start, type, dir, caseSensitive);
260
261         if (cit == bibkeys.end())
262                 return;
263
264         int const found = int(cit - bibkeys.begin()) + 1;
265         if (found == sel)
266                 return;
267
268         // Update the display
269         int const top = max(found - 5, 1);
270         fl_set_browser_topline(dialog_->browser_bib, top);
271         fl_select_browser_line(dialog_->browser_bib, found);
272         input(dialog_->browser_bib, 0);
273 }
274
275
276 ButtonPolicy::SMInput FormCitation::input(FL_OBJECT * ob, long)
277 {
278         ButtonPolicy::SMInput activate = ButtonPolicy::SMI_NOOP;
279
280         biblio::InfoMap const & theMap = controller().bibkeysInfo();
281
282         string topCitekey;
283         if (!citekeys.empty()) topCitekey = citekeys[0];
284
285         if (ob == dialog_->browser_bib) {
286                 fl_deselect_browser(dialog_->browser_cite);
287
288                 unsigned int const sel = fl_get_browser(dialog_->browser_bib);
289                 if (sel < 1 || sel > bibkeys.size())
290                         return ButtonPolicy::SMI_NOOP;
291
292                 // Put into browser_info the additional info associated with
293                 // the selected browser_bib key
294                 fl_clear_browser(dialog_->browser_info);
295
296                 string const tmp = formatted(biblio::getInfo(theMap,
297                                                              bibkeys[sel - 1]),
298                                               dialog_->browser_info->w - 10);
299                 fl_add_browser_line(dialog_->browser_info, tmp.c_str());
300
301                 // Highlight the selected browser_bib key in browser_cite if
302                 // present
303                 vector<string>::const_iterator cit =
304                         find(citekeys.begin(), citekeys.end(), bibkeys[sel - 1]);
305
306                 if (cit != citekeys.end()) {
307                         int const n = int(cit - citekeys.begin());
308                         fl_select_browser_line(dialog_->browser_cite, n + 1);
309                         fl_set_browser_topline(dialog_->browser_cite, n + 1);
310                 }
311
312                 if (!controller().bufferIsReadonly()) {
313                         if (cit != citekeys.end()) {
314                                 setBibButtons(OFF);
315                                 setCiteButtons(ON);
316                         } else {
317                                 setBibButtons(ON);
318                                 setCiteButtons(OFF);
319                         }
320                 }
321
322         } else if (ob == dialog_->browser_cite) {
323                 unsigned int const sel = fl_get_browser(dialog_->browser_cite);
324                 if (sel < 1 || sel > citekeys.size())
325                         return ButtonPolicy::SMI_NOOP;
326
327                 if (!controller().bufferIsReadonly()) {
328                         setBibButtons(OFF);
329                         setCiteButtons(ON);
330                 }
331
332                 // Highlight the selected browser_cite key in browser_bib
333                 vector<string>::const_iterator cit =
334                         find(bibkeys.begin(), bibkeys.end(), citekeys[sel - 1]);
335
336                 if (cit != bibkeys.end()) {
337                         int const n = int(cit - bibkeys.begin());
338                         fl_select_browser_line(dialog_->browser_bib, n + 1);
339                         fl_set_browser_topline(dialog_->browser_bib, n + 1);
340
341                         // Put into browser_info the additional info associated
342                         // with the selected browser_cite key
343                         fl_clear_browser(dialog_->browser_info);
344                         string const tmp =
345                                 formatted(biblio::getInfo(theMap,
346                                                           citekeys[sel - 1]),
347                                           dialog_->browser_info->w - 10);
348                         fl_add_browser_line(dialog_->browser_info, tmp.c_str());
349                 }
350
351         } else if (ob == dialog_->button_add) {
352                 unsigned int const sel = fl_get_browser(dialog_->browser_bib);
353                 if (sel < 1 || sel > bibkeys.size())
354                         return ButtonPolicy::SMI_NOOP;
355
356                 // Add the selected browser_bib key to browser_cite
357                 fl_addto_browser(dialog_->browser_cite,
358                                   bibkeys[sel - 1].c_str());
359                 citekeys.push_back(bibkeys[sel - 1]);
360
361                 int const n = int(citekeys.size());
362                 fl_select_browser_line(dialog_->browser_cite, n);
363
364                 setBibButtons(OFF);
365                 setCiteButtons(ON);
366                 activate = ButtonPolicy::SMI_VALID;
367
368         } else if (ob == dialog_->button_del) {
369                 unsigned int const sel = fl_get_browser(dialog_->browser_cite);
370                 if (sel < 1 || sel > citekeys.size())
371                         return ButtonPolicy::SMI_NOOP;
372
373                 // Remove the selected key from browser_cite
374                 fl_delete_browser_line(dialog_->browser_cite, sel) ;
375                 citekeys.erase(citekeys.begin() + sel - 1);
376
377                 setBibButtons(ON);
378                 setCiteButtons(OFF);
379                 activate = ButtonPolicy::SMI_VALID;
380
381         } else if (ob == dialog_->button_up) {
382                 unsigned int const sel = fl_get_browser(dialog_->browser_cite);
383                 if (sel < 2 || sel > citekeys.size())
384                         return ButtonPolicy::SMI_NOOP;
385
386                 // Move the selected key up one line
387                 vector<string>::iterator it = citekeys.begin() + sel - 1;
388                 string const tmp = *it;
389
390                 fl_delete_browser_line(dialog_->browser_cite, sel);
391                 citekeys.erase(it);
392
393                 fl_insert_browser_line(dialog_->browser_cite, sel - 1, tmp.c_str());
394                 fl_select_browser_line(dialog_->browser_cite, sel - 1);
395                 citekeys.insert(it - 1, tmp);
396                 setCiteButtons(ON);
397                 activate = ButtonPolicy::SMI_VALID;
398
399         } else if (ob == dialog_->button_down) {
400                 unsigned int const sel = fl_get_browser(dialog_->browser_cite);
401                 if (sel < 1 || sel > citekeys.size() - 1)
402                         return ButtonPolicy::SMI_NOOP;
403
404                 // Move the selected key down one line
405                 vector<string>::iterator it = citekeys.begin() + sel - 1;
406                 string const tmp = *it;
407
408                 fl_delete_browser_line(dialog_->browser_cite, sel);
409                 citekeys.erase(it);
410
411                 fl_insert_browser_line(dialog_->browser_cite, sel+1, tmp.c_str());
412                 fl_select_browser_line(dialog_->browser_cite, sel+1);
413                 citekeys.insert(it+1, tmp);
414                 setCiteButtons(ON);
415                 activate = ButtonPolicy::SMI_VALID;
416
417         } else if (ob == dialog_->button_previous) {
418                 findBiblio(biblio::BACKWARD);
419         } else if (ob == dialog_->button_next) {
420                 findBiblio(biblio::FORWARD);
421         } else if (ob == dialog_->input_search) {
422                 findBiblio(biblio::FORWARD);
423         } else if (ob == dialog_->choice_style ||
424                    ob == dialog_->check_full_author_list ||
425                    ob == dialog_->check_force_uppercase ||
426                    ob == dialog_->input_before ||
427                    ob == dialog_->input_after) {
428                 activate = ButtonPolicy::SMI_VALID;
429         }
430
431         string currentCitekey;
432         if (!citekeys.empty())
433                 currentCitekey = citekeys[0];
434
435         if (topCitekey != currentCitekey) {
436                 int choice = std::max(1, fl_get_choice(dialog_->choice_style));
437                 fillChoice(dialog_.get(),
438                            controller().getCiteStrings(currentCitekey));
439                 fl_set_choice(dialog_->choice_style, choice);
440         }
441
442         return activate;
443 }
444
445
446 void FormCitation::update()
447 {
448         // Make the list of all available bibliography keys
449         bibkeys = biblio::getKeys(controller().bibkeysInfo());
450         updateBrowser(dialog_->browser_bib, bibkeys);
451
452         // Ditto for the keys cited in this inset
453         citekeys = getVectorFromString(controller().params().getContents());
454         updateBrowser(dialog_->browser_cite, citekeys);
455
456         // Use the first citekey to fill choice_style
457         string key;
458         if (!citekeys.empty()) key = citekeys[0];
459
460         fillChoice(dialog_.get(), controller().getCiteStrings(key));
461
462         // Use the citation command to update the GUI
463         updateStyle(dialog_.get(), controller().params().getCmdName());
464
465         bool const natbib = controller().usingNatbib();
466         setEnabled(dialog_->check_full_author_list, natbib);
467         setEnabled(dialog_->check_force_uppercase, natbib);
468         setEnabled(dialog_->choice_style, natbib);
469
470         // No keys have been selected yet, so...
471         fl_clear_browser(dialog_->browser_info);
472         setBibButtons(OFF);
473         setCiteButtons(OFF);
474
475         // Natbib can have comments before and after the citation.
476         // This is not yet supported. After only.
477         fl_set_input(dialog_->input_after,
478                      controller().params().getOptions().c_str());
479
480         fl_set_input(dialog_->input_before, _("Not yet supported"));
481         setEnabled(dialog_->input_before, false);
482 }
483
484
485 void FormCitation::updateBrowser(FL_OBJECT * browser,
486                                  vector<string> const & keys) const
487 {
488         // Check whether the current contents of the browser will be
489         // changed by loading the contents of the vec...
490         vector<string> browser_keys = getVector(browser);
491
492         if (browser_keys == keys)
493                 return;
494
495         // They will be changed. Proceed.
496         fl_clear_browser(browser);
497
498         for (vector<string>::const_iterator it = keys.begin();
499              it < keys.end(); ++it) {
500                 string key = trim(*it);
501                 if (!key.empty())
502                         fl_add_browser_line(browser, key.c_str());
503         }
504 }
505
506
507 void FormCitation::setBibButtons(State status) const
508 {
509         setEnabled(dialog_->button_add, (status == ON));
510 }
511
512
513 void FormCitation::setCiteButtons(State status) const
514 {
515         int const sel     = fl_get_browser(dialog_->browser_cite);
516         int const maxline = fl_get_browser_maxline(dialog_->browser_cite);
517         bool const activate      = (status == ON);
518         bool const activate_up   = (activate && sel != 1);
519         bool const activate_down = (activate && sel != maxline);
520
521         setEnabled(dialog_->button_del,  activate);
522         setEnabled(dialog_->button_up,   activate_up);
523         setEnabled(dialog_->button_down, activate_down);
524 }