]> git.lyx.org Git - lyx.git/blob - src/minibuffer.C
Compile fix following this weekend's commits.
[lyx.git] / src / minibuffer.C
1 /* ###########################################################################
2  *
3  *                 The MiniBuffer Class
4  *                 read minibuffer.h for more
5  *                 information.
6  * 
7  *           Copyright 1995 Matthias Ettrich
8  *           Copyright 1995-2001 The LyX Team.  
9  * 
10  * ###########################################################################
11  */
12
13 #include <config.h>
14
15 #include <iostream>
16
17 #ifdef __GNUG__
18 #pragma implementation
19 #endif
20
21 // FIXME: temporary 
22 #include "frontends/xforms/DropDown.h"
23  
24 #include "minibuffer.h"
25
26 #include "support/lyxalgo.h"
27 #include "support/filetools.h"
28 #include "support/lstrings.h"
29 #include "LyXView.h"
30 #include "XFormsView.h"
31 #include "gettext.h"
32 #include "LyXAction.h"
33 #include "BufferView.h"
34
35
36 using SigC::slot;
37 using std::vector;
38
39 extern LyXAction lyxaction;
40
41
42 namespace {
43
44 struct prefix {
45         string p;
46         prefix(string const & s) 
47                 : p(s) {}
48         bool operator()(string const & s) const {
49                 return prefixIs(s, p);
50         }
51 };
52
53 } // end of anon namespace
54
55
56 MiniBuffer::MiniBuffer(LyXView * o, FL_Coord x, FL_Coord y,
57                        FL_Coord h, FL_Coord w)
58         : stored_(false), owner_(o), state_(spaces)
59 {
60         add(FL_NORMAL_INPUT, x, y, h, w);
61         timer.setTimeout(6000);
62         timer.timeout.connect(slot(this, &MiniBuffer::init));
63         stored_timer.setTimeout(1500);
64         stored_timer.timeout.connect(slot(this, &MiniBuffer::stored_slot));
65         deactivate();
66 }
67
68  
69 // thanks for nothing, xforms (recursive creation not allowed) 
70 void MiniBuffer::dd_init()
71 {
72         dropdown_ = new DropDown(owner_, the_buffer);
73         dropdown_->result.connect(slot(this, &MiniBuffer::set_complete_input));
74         dropdown_->keypress.connect(slot(this, &MiniBuffer::append_char));
75 }
76
77
78 MiniBuffer::~MiniBuffer()
79 {
80         delete dropdown_;
81 }
82
83  
84 void MiniBuffer::stored_slot() 
85 {
86         if (stored_) {
87                 stored_ = false;
88                 set_input(stored_input);
89         }
90 }
91
92
93 void MiniBuffer::stored_set(string const & str) 
94 {
95         stored_input = str;
96         stored_ = true;
97         stored_timer.start();
98 }
99
100
101 int MiniBuffer::peek_event(FL_OBJECT * ob, int event, int key)
102 {
103         switch (event) {
104         case FL_UNFOCUS:
105                 deactivate();
106                 break;
107         case FL_KEYBOARD:
108         {
109                 char const * tmp = fl_get_input(ob);
110                 string input = tmp ? tmp : "";
111                 if (stored_) {
112                         stored_timer.stop();
113                         input = stored_input;
114                         set_input(input);
115                         stored_ = false;
116                 }
117                 
118                 switch (key) {
119                 case XK_Down:
120                         if (hist_iter != history_->end()) {
121                                 ++hist_iter;
122                         }
123                         if (hist_iter == history_->end()) {
124                                 // no further history
125                                 stored_set(input);
126                                 set_input(_("[End of history]"));
127                         } else {
128                                 set_input((*hist_iter));
129                         }
130                         return 1; 
131                 case XK_Up:
132                         if (hist_iter == history_->begin()) {
133                                 // no further history
134                                 stored_set(input);
135                                 set_input(_("[Beginning of history]"));
136                         } else {
137                                 --hist_iter;
138                                 set_input((*hist_iter));
139                         }
140                         return 1; 
141                 case 9:
142                 case XK_Tab:
143                 {
144                         // Completion handling.
145                         
146                         vector<string> comp;
147                         lyx::copy_if(completion_.begin(),
148                                      completion_.end(),
149                                      std::back_inserter(comp), prefix(input));
150
151                         if (comp.empty()) {
152                                 // No matches
153                                 string const tmp = input + _(" [no match]");
154                                 stored_set(input);
155                                 set_input(tmp);
156                         } else if (comp.size() == 1) {
157                                 // Perfect match
158                                 string const tmp =
159                                         comp[0] + _(" [sole completion]");
160                                 stored_set(comp[0] + " ");
161                                 set_input(tmp);
162                         } else {
163                                 // More that one match
164                                 // Find maximal avaliable prefix
165                                 string const tmp = comp[0];
166                                 string test(input);
167                                 if (tmp.length() > test.length())
168                                         test += tmp[test.length()];
169                                 while (test.length() < tmp.length()) {
170                                         vector<string> vtmp;
171                                         lyx::copy_if(comp.begin(),
172                                                      comp.end(),
173                                                      std::back_inserter(vtmp),
174                                                      prefix(test));
175                                         if (vtmp.size() != comp.size()) {
176                                                 test.erase(test.length() - 1);
177                                                 break;
178                                         }
179                                         test += tmp[test.length()];
180                                 }
181                                 set_input(test);
182                                 
183                                 int x,y,w,h;
184                                 fl_get_wingeometry(fl_get_real_object_window(the_buffer),
185                                         &x, &y, &w, &h);
186  
187                                 // asynchronous completion
188                                 int const air = the_buffer->x;
189                                 x += air;
190                                 y += h - (the_buffer->h + air);
191                                 w = the_buffer->w;
192                                 dropdown_->select(comp, x, y, w);
193                         }
194                         return 1; 
195                 }
196                 case 27:
197                 case XK_Escape:
198                         // Abort
199                         owner_->view()->focus(true);
200                         init();
201                         deactivate();
202                         //escape.emit();
203                         return 1; 
204                 case 13:
205                 case XK_Return:
206                 {
207 #if 0
208                         // This will go in again in a little while
209                         // we need to be able to declare what types
210                         // of argumetns LFUN's should have first. (Lgb)
211                         // First check for match
212                         vector<string>::const_iterator cit =
213                                 std::find(completion_.begin(),
214                                           completion_.end(),
215                                           input);
216                         if (cit == completion_.end()) {
217                                 // no such func/item
218                                 stored_set(input);
219                                 string const tmp = input + _(" [no match]");
220                                 set_input(tmp);
221                         } else {
222 #endif
223                                 // Return the inputted string
224                                 deactivate();
225                                 owner_->view()->focus(true);
226                                 if (!input.empty()) {
227                                         history_->push_back(input);
228                                 }
229                                 stringReady.emit(input);
230 # if 0
231                         }
232 #endif
233                         return 1;
234                 }
235                 case XK_space:
236                 {
237                         // Depending on the input state spaces might not
238                         // be allowed.
239                         switch (state_) {
240                         case spaces:
241                                 return 0;
242                         case nospaces:
243                         {
244                                 stored_set(input);
245                                 string const tmp = input + _(" [no match]");
246                                 set_input(tmp);
247                                 return 1;
248                         }
249                         }
250                         
251                 }
252                 
253                 default:
254                         return 0;
255                 }
256         }
257         default:
258                 //lyxerr << "Unhandled minibuffer event!" << endl;
259                 break;
260         }
261         
262         return 0;
263 }
264
265
266 extern "C" {
267         
268         static
269         int C_MiniBuffer_peek_event(FL_OBJECT * ob, int event, 
270                                     FL_Coord, FL_Coord,
271                                     int key, void * /*xev*/)
272         {
273                 MiniBuffer * mini = static_cast<MiniBuffer*>(ob->u_vdata);
274                 return mini->peek_event(ob, event, key);
275         }
276         
277 }
278
279
280 void MiniBuffer::prepare()
281 {
282         text.erase();
283         set_input("");
284         activate();
285         fl_set_focus_object(static_cast<XFormsView *>(owner_)->getForm(),
286                             the_buffer);
287 }
288
289
290 FL_OBJECT * MiniBuffer::add(int type, FL_Coord x, FL_Coord y,
291                            FL_Coord w, FL_Coord h)
292 {
293         FL_OBJECT * obj;
294         
295         the_buffer = obj = fl_add_input(type, x, y, w, h, text.c_str());
296         fl_set_object_boxtype(obj, FL_DOWN_BOX);
297         fl_set_object_resize(obj, FL_RESIZE_ALL);
298         fl_set_object_gravity(obj, SouthWestGravity, SouthEastGravity);
299         fl_set_object_color(obj, FL_MCOL, FL_MCOL);
300         fl_set_object_lsize(obj, FL_NORMAL_SIZE);
301         
302         // To intercept Up, Down, Table for history
303         fl_set_object_prehandler(obj, C_MiniBuffer_peek_event);
304         obj->u_vdata = this;
305         obj->wantkey = FL_KEY_TAB;
306
307         set_input(text);
308         
309         return obj;
310 }
311
312
313 void MiniBuffer::message(string const & str) 
314 {
315         timer.restart();
316         string const ntext = strip(str);
317         if (!the_buffer->focus) {
318                 set_input(ntext);
319                 text = ntext;
320         }
321 }
322
323
324 void MiniBuffer::messagePush(string const & str) 
325 {
326         text_stored = text;
327         message(str);
328 }
329
330
331 void MiniBuffer::messagePop()
332 {
333         if (!text_stored.empty()) {
334                 message(text_stored);
335                 text_stored.erase();
336         }
337 }
338
339
340 void MiniBuffer::addSet(string const & s1, string const & s2)
341 {
342         string const str = text + ' ' +  s1 + ' ' + s2;
343         message(str);
344 }
345
346
347 void MiniBuffer::getString(State spaces,
348                            vector<string> const & completion,
349                            vector<string> & history)
350 {
351         state_ = spaces;
352         completion_ = completion;
353         history_ = &history;
354         hist_iter = history_->end();
355         prepare();
356 }
357
358
359 void MiniBuffer::init()
360 {
361         // If we have focus, we don't want to change anything.
362         if (the_buffer->focus)
363                 return;
364
365         timeout.emit();
366         timer.stop();
367 }
368
369
370 void MiniBuffer::activate()
371 {
372         fl_activate_object(the_buffer);
373         redraw();
374 }
375
376
377 void MiniBuffer::deactivate()
378 {
379         redraw();
380         fl_deactivate_object(the_buffer);
381         XFlush(fl_display);
382 }
383
384
385 void MiniBuffer::redraw() 
386 {
387         fl_redraw_object(the_buffer);
388         XFlush(fl_display);
389 }
390
391
392 void MiniBuffer::set_complete_input(string const & str)
393 {
394         if (!str.empty()) {
395                 // add a space so the user can type
396                 // an argument immediately
397                 set_input(str + " ");
398         }
399 }
400
401  
402 void MiniBuffer::append_char(char c)
403 {
404         if (!c || !isprint(c))
405                 return;
406
407         char const * tmp = fl_get_input(the_buffer);
408         string str = tmp ? tmp : "";
409
410         str += c;
411
412         fl_set_input(the_buffer, str.c_str());
413 }
414
415  
416 void MiniBuffer::set_input(string const & str)
417 {
418         fl_set_input(the_buffer, str.c_str());
419         XFlush(fl_display);
420 }