]> git.lyx.org Git - lyx.git/blob - src/frontends/gnome/FormCitation.C
regex patch for gnom from Marko
[lyx.git] / src / frontends / gnome / FormCitation.C
1 // -*- C++ -*-
2 /* This file is part of
3  * ====================================================== 
4  *
5  *           LyX, The Document Processor
6  *
7  *           Copyright 2000 The LyX Team.
8  *
9  * ======================================================
10  */
11
12 #include <config.h>
13
14 #include <algorithm>
15
16 #ifdef __GNUG__
17 #pragma implementation
18 #endif
19
20
21 #include "gettext.h"
22 #include "Dialogs.h"
23 #include "FormCitation.h"
24 #include "LyXView.h"
25 #include "buffer.h"
26 #include "lyxfunc.h"
27 #include "support/filetools.h"
28 #include "support/LRegex.h"
29
30 extern "C" {
31 #include "diainsertcitation_interface.h"
32 #include "support.h"
33 }
34
35 #include <gtk--/scrolledwindow.h>
36 #include <gnome--/pixmap.h>
37
38 using std::vector;
39 using std::pair;
40 using std::max;
41 using std::min;
42 using std::find;
43
44 #ifdef SIGC_CXX_NAMESPACES
45 using SigC::slot;
46 using SigC::bind;
47 #endif
48
49 // gnome configuration file keys
50 #define LOCAL_CONFIGURE_PREFIX "FormCitation"
51
52 #define CONF_DIALOG_WIDTH               "width"
53 #define CONF_DIALOG_WIDTH_DEFAULT       "=550"
54
55 #define CONF_DIALOG_HEIGTH              "heigth"
56 #define CONF_DIALOG_HEIGTH_DEFAULT      "=550"
57
58 #define CONF_PANE_INFO                  "paneinfo"
59 #define CONF_PANE_INFO_DEFAULT          "=300"
60
61 #define CONF_PANE_KEY                   "panekey"
62 #define CONF_PANE_KEY_DEFAULT           "=225"
63
64 #define CONF_COLUMN                     "column"
65 #define CONF_COLUMN_DEFAULT             "=50"
66
67 #define CONF_REGEXP                     "regexp"
68 #define CONF_REGEXP_DEFAULT             "=0"
69
70
71 FormCitation::FormCitation(LyXView * lv, Dialogs * d)
72         : lv_(lv), d_(d), u_(0), h_(0), ih_(0), inset_(0), dialog_(NULL)
73 {
74   // let the dialog be shown
75   // These are permanent connections so we won't bother
76   // storing a copy because we won't be disconnecting.
77   d->showCitation.connect(slot(this, &FormCitation::showInset));
78   d->createCitation.connect(slot(this, &FormCitation::createInset));
79 }
80
81
82 FormCitation::~FormCitation()
83 {
84   hide();
85 }
86
87 void FormCitation::showInset( InsetCommand * const inset )
88 {
89   if( dialog_!=NULL || inset == 0 ) return;
90   
91   inset_ = inset;
92   ih_ = inset_->hide.connect(slot(this, &FormCitation::hide));
93   
94   params = inset->params();
95   show();
96 }
97
98
99 void FormCitation::createInset( string const & arg )
100 {
101   if( dialog_!=NULL ) return;
102   
103   params.setFromString( arg );
104   show();
105 }
106
107 static
108 void parseBibTeX(string data,
109                  string const & findkey,
110                  string & keyvalue)
111 {
112   unsigned int i;
113
114   keyvalue = "";
115   
116   for (i=0; i<data.length(); ++i)
117     if (data[i]=='\n' || data[i]=='\t')
118       data[i] = ' ';
119   
120   data = frontStrip(data);
121   while (!data.empty()
122          && data[0]!='='
123          && (data.find(' ')!=string::npos ||
124              data.find('=')!=string::npos) )
125     {
126       unsigned int keypos = min(data.find(' '), data.find('='));
127       string key = lowercase( data.substr(0, keypos) );
128       string value, tmp;
129       char enclosing;
130       
131       data = data.substr(keypos, data.length()-1);
132       data = frontStrip(strip(data));
133       if (data.length() > 1 && data[0]=='=')
134         {
135           data = frontStrip(data.substr(1, data.length()-1));
136           if (!data.empty())
137             {
138               keypos = 1;
139               if (data[0]=='{') enclosing = '}';
140               else if (data[0]=='"') enclosing = '"';
141               else { keypos=0; enclosing=' '; }
142
143               if (keypos &&
144                   data.find(enclosing)!=string::npos &&
145                   data.length()>1)
146                 {
147                   tmp = data.substr(keypos, data.length()-1);
148                   while (tmp.find('{')!=string::npos &&
149                          tmp.find('}')!=string::npos &&
150                          tmp.find('{') < tmp.find('}') &&
151                          tmp.find('{') < tmp.find(enclosing))
152                     {
153                       keypos += tmp.find('{')+1;
154                       tmp = data.substr(keypos, data.length()-1);
155                       keypos += tmp.find('}')+1;
156                       tmp = data.substr(keypos, data.length()-1);
157                     }
158
159                   if (tmp.find(enclosing)==string::npos) return;
160                   else
161                     {
162                       keypos += tmp.find(enclosing);
163                       tmp = data.substr(keypos, data.length()-1);
164                     }
165
166                   value = data.substr(1, keypos-1);
167
168                   if (keypos+1<data.length()-1) data = frontStrip(data.substr(keypos+1, data.length()-1));
169                   else data = "";
170                 }
171               else if (!keypos &&
172                        (data.find(' ') ||
173                         data.find(','))
174                        ) // numerical value ?
175                 {
176                   keypos = data.length()-1;
177                   if (data.find(' ')!=string::npos) keypos = data.find(' ');
178                   if (data.find(',')!=string::npos &&
179                       keypos > data.find(','))
180                     keypos = data.find(',');
181
182                   value = data.substr(0, keypos);
183                   
184                   if (keypos+1<data.length()-1) data = frontStrip(data.substr(keypos+1, data.length()-1));
185                   else data = "";
186                 }
187               else return;
188
189               if (findkey == key) { keyvalue = value; return; } 
190
191               data = frontStrip(frontStrip(data,','));
192             }
193         }
194       else return;
195     }
196 }
197
198 void FormCitation::show()
199 {
200   if (!dialog_)
201     {
202       GtkWidget * pd = create_DiaInsertCitation();
203
204       dialog_ = Gtk::wrap(pd);
205       clist_selected_ = Gtk::wrap( GTK_CLIST( lookup_widget(pd, "clist_selected") ) );
206       info_ = Gtk::wrap( GNOME_LESS( lookup_widget(pd, "info") ) );
207       text_after_ = Gtk::wrap( GNOME_ENTRY( lookup_widget(pd, "text_after") ) );
208       search_text_ = Gtk::wrap( GNOME_ENTRY( lookup_widget(pd, "search_text") ) );
209         
210       button_select_ = Gtk::wrap( GTK_BUTTON( lookup_widget(pd, "button_select") ) );
211       button_unselect_ = Gtk::wrap( GTK_BUTTON( lookup_widget(pd, "button_unselect") ) );
212       button_up_ = Gtk::wrap( GTK_BUTTON( lookup_widget(pd, "button_up") ) );
213       button_down_ = Gtk::wrap( GTK_BUTTON( lookup_widget(pd, "button_down") ) );
214       button_search_ = Gtk::wrap( GTK_BUTTON( lookup_widget(pd, "button_search") ) );
215
216       button_regexp_ = Gtk::wrap( GTK_CHECK_BUTTON( lookup_widget(pd, "button_regexp") ) );
217
218       paned_info_ = Gtk::wrap( GTK_PANED( lookup_widget(pd, "vpaned_info") ) );
219       paned_key_ = Gtk::wrap( GTK_PANED( lookup_widget(pd, "hpaned_key") ) );
220       box_keys_ =  Gtk::wrap( GTK_BOX( lookup_widget(pd, "vbox_keys") ) );
221
222       b_ok = Gtk::wrap( GTK_BUTTON( lookup_widget(pd, "button_ok") ) );
223       b_cancel = Gtk::wrap( GTK_BUTTON( lookup_widget(pd, "button_cancel") ) );
224
225       // constructing and packing CList
226       vector<string> colnames;
227       colnames.push_back("INVISIBLE");
228       colnames.push_back(N_("Key"));
229       colnames.push_back(N_("Author(s)"));
230       colnames.push_back(N_("Title"));
231       colnames.push_back(N_("Year"));
232       colnames.push_back(N_("Journal"));
233       clist_bib_ = manage( new Gtk::CList(colnames) );
234       clist_bib_->column(0).set_visiblity(false);
235       
236       Gtk::ScrolledWindow * sw_ = Gtk::wrap( GTK_SCROLLED_WINDOW( lookup_widget(pd, "scrolledwindow_bib") ) );
237       sw_->add(*clist_bib_);
238
239       // populating buttons with icons
240       Gnome::Pixmap * p;
241       p = Gtk::wrap( GNOME_PIXMAP( gnome_stock_pixmap_widget(NULL, GNOME_STOCK_PIXMAP_BACK) ) ); 
242       button_select_->add(*p);
243       p = Gtk::wrap( GNOME_PIXMAP( gnome_stock_pixmap_widget(NULL, GNOME_STOCK_PIXMAP_TRASH) ) ); 
244       button_unselect_->add(*p);
245       p = Gtk::wrap( GNOME_PIXMAP( gnome_stock_pixmap_widget(NULL, GNOME_STOCK_PIXMAP_UP) ) ); 
246       button_up_->add(*p);
247       p = Gtk::wrap( GNOME_PIXMAP( gnome_stock_pixmap_widget(NULL, GNOME_STOCK_PIXMAP_DOWN) ) ); 
248       button_down_->add(*p);
249       
250
251       // connecting signals
252       clist_bib_->click_column.connect(slot(this, &FormCitation::sortBibList));
253
254       clist_selected_->select_row.connect(bind(slot(this, &FormCitation::selection_toggled),
255                                                true, true));
256       clist_bib_->select_row.connect(bind(slot(this, &FormCitation::selection_toggled),
257                                           true, false));
258       clist_selected_->unselect_row.connect(bind(slot(this, &FormCitation::selection_toggled),
259                                                  false, true));
260       clist_bib_->unselect_row.connect(bind(slot(this, &FormCitation::selection_toggled),
261                                             false, false));
262       
263       button_select_->clicked.connect(slot(this, &FormCitation::newCitation));
264       button_unselect_->clicked.connect(slot(this, &FormCitation::removeCitation));
265       button_up_->clicked.connect(slot(this, &FormCitation::moveCitationUp));
266       button_down_->clicked.connect(slot(this, &FormCitation::moveCitationDown));
267
268       search_text_->get_entry()->activate.connect(slot(this, &FormCitation::search));
269       button_search_->clicked.connect(slot(this, &FormCitation::search));
270       
271       b_ok->clicked.connect(slot(this, &FormCitation::apply));
272       b_ok->clicked.connect(dialog_->destroy.slot());
273       b_cancel->clicked.connect(dialog_->destroy.slot());
274       dialog_->destroy.connect(slot(this, &FormCitation::free));
275
276       u_ = d_->updateBufferDependent.connect(slot(this, &FormCitation::update));
277       h_ = d_->hideBufferDependent.connect(slot(this, &FormCitation::hide));
278
279       // setting sizes of the widgets
280       string path;
281       string w, h;
282       path  += PACKAGE "/" LOCAL_CONFIGURE_PREFIX;
283       w = path + "/" + CONF_DIALOG_WIDTH + CONF_DIALOG_WIDTH_DEFAULT;
284       h = path + "/" + CONF_DIALOG_HEIGTH + CONF_DIALOG_HEIGTH_DEFAULT;
285       dialog_->set_usize(gnome_config_get_int(w.c_str()),
286                          gnome_config_get_int(h.c_str()));
287
288       w = path + "/" + CONF_PANE_INFO + CONF_PANE_INFO_DEFAULT;
289       paned_info_->set_position( gnome_config_get_int(w.c_str()) );
290
291       w = path + "/" + CONF_PANE_KEY + CONF_PANE_KEY_DEFAULT;
292       paned_key_->set_position( gnome_config_get_int(w.c_str()) );
293
294       int i, sz;
295       for (i = 0, sz = clist_bib_->columns().size(); i < sz; ++i)
296         {
297           w = path + "/" + CONF_COLUMN + "_" + tostr(i) + CONF_COLUMN_DEFAULT;
298           clist_bib_->column(i).set_width( gnome_config_get_int(w.c_str()) );
299         }
300
301       // restoring regexp setting
302       w = path + "/" + CONF_REGEXP + CONF_REGEXP_DEFAULT;
303       button_regexp_->set_active( (gnome_config_get_int(w.c_str()) > 0) );
304       
305       // ready to go...
306       if (!dialog_->is_visible()) dialog_->show_all();
307
308       update();  // make sure its up-to-date
309     }
310   else
311     {
312       Gdk_Window dialog_win(dialog_->get_window());
313       dialog_win.raise();
314     }
315 }
316
317 void FormCitation::addItemToBibList(int i)
318 {
319   vector<string> r;
320   string key, info;
321   string val;
322
323   key = bibkeys[i];
324   info = bibkeysInfo[i];
325
326   // don't change the order of these first two items:
327   // callback functions depend on the data stored in the first column (its hided)
328   // and in the second column (shown to user)
329   r.push_back( tostr(i) ); 
330   r.push_back( key );
331   
332   // this can be changed (configured by user?)
333   parseBibTeX( info, "author", val);  r.push_back(val);
334   parseBibTeX( info, "title", val);  r.push_back(val);
335   parseBibTeX( info, "year", val);  r.push_back(val);
336   parseBibTeX( info, "journal", val);  r.push_back(val);
337   
338   clist_bib_->rows().push_back(r);
339 }
340
341 void FormCitation::update()
342 {
343   bibkeys.clear();
344   bibkeysInfo.clear();
345
346   clist_selected_->rows().clear();
347   clist_bib_->rows().clear();
348
349   // populating clist_bib_
350   clist_bib_->freeze();
351
352   vector<pair<string,string> > blist =
353     lv_->buffer()->getBibkeyList();
354
355   int i, sz;
356   for ( i = 0, sz = blist.size(); i < sz; ++i )
357     {
358       bibkeys.push_back(blist[i].first);
359       bibkeysInfo.push_back(blist[i].second);
360     }
361
362   blist.clear();
363
364   for ( i = 0, sz = bibkeys.size(); i < sz; ++i )
365     addItemToBibList(i);
366
367   clist_bib_->sort();
368   clist_bib_->thaw();
369   // clist_bib_: done
370
371   // populating clist_selected_
372   vector<string> r;
373   string tmp, keys( params.getContents() );
374   keys = frontStrip( split(keys, tmp, ',') );
375   while( !tmp.empty() )
376     {
377       r.clear();
378       r.push_back(tmp);
379       clist_selected_->rows().push_back(r);
380
381       keys = frontStrip( split(keys, tmp, ',') );
382     }
383   // clist_selected_: done
384
385   text_after_->get_entry()->set_text(params.getOptions());
386   
387   updateButtons();
388 }
389
390 void FormCitation::updateButtons()
391 {
392   bool sens;
393
394   sens = (clist_selected_->selection().size()>0);
395   button_unselect_->set_sensitive(sens);
396   button_up_->set_sensitive(sens &&
397                             clist_selected_->selection().operator[](0).get_row_num()>0);
398   button_down_->set_sensitive(sens &&
399                               clist_selected_->selection().operator[](0).get_row_num() <
400                               clist_selected_->rows().size()-1);
401
402   sens = (clist_bib_->selection().size()>0);
403   button_select_->set_sensitive( (clist_bib_->selection().size()>0) );
404 }
405
406 void FormCitation::selection_toggled(gint            row,
407                                      gint            ,//column,
408                                      GdkEvent        * ,//event,
409                                      bool selected,
410                                      bool citeselected)
411 {
412   if (selected)
413     {
414       bool keyfound = false;
415       string info;
416       if (citeselected)
417         {
418           // lookup the record with the same key in bibkeys and show additional Info
419           int i;
420           int sz = bibkeys.size();
421           string key = clist_selected_->cell(row,0).get_text();
422           for (i=0; !keyfound && i<sz; ++i)
423             if (bibkeys[i] == key)
424               {
425                 info = bibkeysInfo[i];
426                 keyfound = true;
427               }   
428         }
429       else
430         {
431           // the first column in clist_bib_ contains the index
432           keyfound = true;
433           info = bibkeysInfo[ strToInt(clist_bib_->cell(row,0).get_text()) ];
434         }
435
436       if (keyfound)
437         info_->show_string(info);
438       else
439         info_->show_string(N_("--- No such key in the database ---"));
440     }
441   else
442     {
443       info_->show_string("");
444     }
445   updateButtons();
446 }
447
448 void FormCitation::removeCitation()
449 {
450   clist_selected_->rows().remove(clist_selected_->selection().operator[](0));
451   updateButtons();
452 }
453
454 void FormCitation::moveCitationUp()
455 {
456   int i = clist_selected_->selection().operator[](0).get_row_num();
457   clist_selected_->swap_rows( i-1, i );
458   clist_selected_->row(i-1).select();
459   updateButtons();
460 }
461
462 void FormCitation::moveCitationDown()
463 {
464   int i = clist_selected_->selection().operator[](0).get_row_num();
465   clist_selected_->swap_rows( i+1, i );
466   clist_selected_->row(i+1).select();
467   updateButtons();
468 }
469
470 void FormCitation::newCitation()
471 {
472   // citation key is in the first column of clist_bib_ list
473   vector<string> r;
474   r.push_back( clist_bib_->selection().operator[](0).operator[](1).get_text() );
475   clist_selected_->rows().push_back(r);
476   clist_selected_->row( clist_selected_->rows().size()-1 ).select();
477   updateButtons();
478 }
479
480 void FormCitation::hide()
481 {
482   if (dialog_!=NULL) dialog_->destroy();
483 }
484
485 void FormCitation::free()
486 {
487   if (dialog_!=NULL)
488     {
489       // storing configuration
490       string path;
491       string w, h;
492       path  = PACKAGE "/" LOCAL_CONFIGURE_PREFIX;
493       w = path + "/" + CONF_DIALOG_WIDTH;
494       h = path + "/" + CONF_DIALOG_HEIGTH;
495
496       gnome_config_set_int(w.c_str(), dialog_->width());
497       gnome_config_set_int(h.c_str(), dialog_->height());
498
499       w = path + "/" + CONF_PANE_INFO;
500       gnome_config_set_int(w.c_str(), paned_key_->height());
501
502       w = path + "/" + CONF_PANE_KEY;
503       gnome_config_set_int(w.c_str(), box_keys_->width());
504
505       int i, sz;
506       for (i = 0, sz = clist_bib_->columns().size(); i < sz; ++i)
507         {
508           w = path + "/" + CONF_COLUMN + "_" + tostr(i);
509           gnome_config_set_int(w.c_str(), clist_bib_->get_column_width(i));
510         }
511
512       w = path + "/" + CONF_REGEXP;
513       gnome_config_set_int(w.c_str(), button_regexp_->get_active());
514
515       gnome_config_sync();
516
517       // cleaning up
518       dialog_ = NULL;
519       u_.disconnect();
520       h_.disconnect();
521       inset_ = 0;
522       ih_.disconnect();
523     }
524 }
525
526 void FormCitation::apply()
527 {
528   if( lv_->buffer()->isReadonly() ) return;
529
530   string contents;
531   for( unsigned int i = 0; i < clist_selected_->rows().size(); ++i )
532     {
533       if (i > 0) contents += ", ";
534       contents += clist_selected_->cell(i, 0).get_text();
535     }
536
537   params.setContents( contents );
538   params.setOptions( text_after_->get_entry()->get_text() );
539
540   if( inset_ != 0 )
541     {
542       // Only update if contents have changed
543       if( params != inset_->params() )
544         {
545           inset_->setParams( params );
546           lv_->view()->updateInset( inset_, true );
547         }
548     }
549   else
550     {
551       lv_->getLyXFunc()->Dispatch( LFUN_CITATION_INSERT,
552                                    params.getAsString().c_str() );
553     }
554 }
555
556 void FormCitation::sortBibList(gint col)
557 {
558   clist_bib_->set_sort_column(col);
559   clist_bib_->sort();
560 }
561
562 void FormCitation::search()
563 {
564   if (button_regexp_->get_active()) searchReg();
565   else searchSimple();
566 }
567
568 // looking for entries which match regexp
569 void FormCitation::searchReg()
570 {
571   string tmp, rexptxt( search_text_->get_entry()->get_text() );
572   rexptxt = frontStrip( strip( rexptxt ) );
573   
574   LRegex reg(rexptxt);
575
576   // populating clist_bib_
577   clist_bib_->rows().clear();
578
579   clist_bib_->freeze();
580
581   int i, sz;
582   bool additem;
583   for ( i = 0, sz = bibkeys.size(); i < sz; ++i )
584     {
585       string data = bibkeys[i] + bibkeysInfo[i];
586
587       if (rexptxt.empty()) additem = true;
588       else additem = (reg.exec(data).size() > 0);
589              
590       if ( additem ) addItemToBibList(i);
591     }
592
593   clist_bib_->sort();
594   clist_bib_->thaw();
595   // clist_bib_: done
596   updateButtons();
597 }
598
599 // looking for entries which contain all the words specified in search_text entry
600 void FormCitation::searchSimple()
601 {
602   vector<string> searchwords;
603   string tmp, stext( search_text_->get_entry()->get_text() );
604   stext = frontStrip( strip( stext ) );
605   stext = frontStrip( split(stext, tmp, ' ') );
606   while( !tmp.empty() )
607     {
608       searchwords.push_back(tmp);
609       stext = frontStrip( split(stext, tmp, ' ') );
610     }
611   
612   // populating clist_bib_
613   clist_bib_->rows().clear();
614
615   clist_bib_->freeze();
616
617   int i, sz;
618   bool additem;
619   for ( i = 0, sz = bibkeys.size(); i < sz; ++i )
620     {
621       string data = bibkeys[i] + bibkeysInfo[i];
622
623       additem = true;
624
625       int j, szs;
626       for (j = 0, szs = searchwords.size();
627            additem && j < szs; ++j )
628         if ( data.find(searchwords[j]) == string::npos )
629           additem = false;
630              
631       if ( additem ) addItemToBibList(i);
632     }
633
634   clist_bib_->sort();
635   clist_bib_->thaw();
636   // clist_bib_: done
637   updateButtons();
638 }