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