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