]> git.lyx.org Git - lyx.git/blob - src/minibuffer.C
Painter and scrollbar API patches
[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                         owner_->view()->focus(true);
213                         init();
214                         deactivate();
215                         //escape.emit();
216                         return 1;
217                 case 13:
218                 case XK_Return:
219                 {
220 #if 0
221                         // This will go in again in a little while
222                         // we need to be able to declare what types
223                         // of argumetns LFUN's should have first. (Lgb)
224                         // First check for match
225                         vector<string>::const_iterator cit =
226                                 find(completion_.begin(),
227                                           completion_.end(),
228                                           input);
229                         if (cit == completion_.end()) {
230                                 // no such func/item
231                                 stored_set(input);
232                                 string const tmp = input + _(" [no match]");
233                                 set_input(tmp);
234                         } else {
235 #endif
236                                 // Return the inputted string
237                                 deactivate();
238                                 owner_->view()->focus(true);
239                                 if (!input.empty()) {
240                                         history_->push_back(input);
241                                 }
242                                 stringReady(input);
243 # if 0
244                         }
245 #endif
246                         return 1;
247                 }
248                 case XK_space:
249                 {
250                         // Depending on the input state spaces might not
251                         // be allowed.
252                         switch (state_) {
253                         case spaces:
254                                 return 0;
255                         case nospaces:
256                         {
257                                 stored_set(input);
258                                 string const tmp = input + _(" [no match]");
259                                 set_input(tmp);
260                                 return 1;
261                         }
262                         }
263
264                 }
265
266                 default:
267                         return 0;
268                 }
269         }
270         default:
271                 //lyxerr << "Unhandled minibuffer event!" << endl;
272                 break;
273         }
274
275         return 0;
276 }
277
278
279 extern "C" {
280
281         static
282         int C_MiniBuffer_peek_event(FL_OBJECT * ob, int event,
283                                     FL_Coord, FL_Coord,
284                                     int key, void * /*xev*/)
285         {
286                 MiniBuffer * mini = static_cast<MiniBuffer*>(ob->u_vdata);
287                 return mini->peek_event(ob, event, key);
288         }
289
290 }
291
292
293 void MiniBuffer::prepare()
294 {
295         text.erase();
296         set_input("");
297         activate();
298         fl_set_focus_object(static_cast<XFormsView *>(owner_)->getForm(),
299                             the_buffer);
300 }
301
302
303 FL_OBJECT * MiniBuffer::add(int type, FL_Coord x, FL_Coord y,
304                            FL_Coord w, FL_Coord h)
305 {
306         FL_OBJECT * obj;
307
308         the_buffer = obj = fl_add_input(type, x, y, w, h, text.c_str());
309         fl_set_object_boxtype(obj, FL_DOWN_BOX);
310         fl_set_object_resize(obj, FL_RESIZE_ALL);
311         fl_set_object_gravity(obj, SouthWestGravity, SouthEastGravity);
312         fl_set_object_color(obj, FL_MCOL, FL_MCOL);
313         fl_set_object_lsize(obj, FL_NORMAL_SIZE);
314
315         // To intercept Up, Down, Table for history
316         fl_set_object_prehandler(obj, C_MiniBuffer_peek_event);
317         obj->u_vdata = this;
318         obj->wantkey = FL_KEY_TAB;
319
320         set_input(text);
321
322         return obj;
323 }
324
325
326 void MiniBuffer::message(string const & str)
327 {
328         timer->restart();
329         string const ntext = strip(str);
330         if (!the_buffer->focus) {
331                 set_input(ntext);
332                 text = ntext;
333         }
334 }
335
336
337 void MiniBuffer::messagePush(string const & str)
338 {
339         text_stored = text;
340         message(str);
341 }
342
343
344 void MiniBuffer::messagePop()
345 {
346         if (!text_stored.empty()) {
347                 message(text_stored);
348                 text_stored.erase();
349         }
350 }
351
352
353 void MiniBuffer::addSet(string const & s1, string const & s2)
354 {
355         string const str = text + ' ' +  s1 + ' ' + s2;
356         message(str);
357 }
358
359
360 void MiniBuffer::getString(State spaces,
361                            vector<string> const & completion,
362                            vector<string> & history)
363 {
364         state_ = spaces;
365         completion_ = completion;
366         history_ = &history;
367         hist_iter = history_->end();
368         prepare();
369 }
370
371
372 void MiniBuffer::init()
373 {
374         // If we have focus, we don't want to change anything.
375         if (the_buffer->focus)
376                 return;
377
378         timeout();
379         timer->stop();
380 }
381
382
383 void MiniBuffer::activate()
384 {
385         fl_activate_object(the_buffer);
386         redraw();
387 }
388
389
390 void MiniBuffer::deactivate()
391 {
392         redraw();
393         fl_deactivate_object(the_buffer);
394         XFlush(fl_display);
395 }
396
397
398 void MiniBuffer::redraw()
399 {
400         fl_redraw_object(the_buffer);
401         XFlush(fl_display);
402 }
403
404
405 void MiniBuffer::set_complete_input(string const & str)
406 {
407         if (!str.empty()) {
408                 // add a space so the user can type
409                 // an argument immediately
410                 set_input(str + " ");
411         }
412 }
413
414
415 void MiniBuffer::append_char(char c)
416 {
417         if (!c || !isprint(c))
418                 return;
419
420         char const * tmp = fl_get_input(the_buffer);
421         string str = tmp ? tmp : "";
422
423         str += c;
424
425         fl_set_input(the_buffer, str.c_str());
426 }
427
428
429 void MiniBuffer::set_input(string const & str)
430 {
431         fl_set_input(the_buffer, str.c_str());
432         XFlush(fl_display);
433 }