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