]> git.lyx.org Git - lyx.git/blob - src/minibuffer.C
5e1f324423498ae8cfe5147d3245c247aee8bae3
[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-2000 The LyX Team.  
9  * 
10  * ###########################################################################
11  */
12
13 #include <config.h>
14
15 #ifdef __GNUG__
16 #pragma implementation
17 #endif
18
19 #include "minibuffer.h"
20
21 #include "support/lyxalgo.h"
22 #include "support/filetools.h"
23 #include "LyXView.h"
24 #include "gettext.h"
25 #include "LyXAction.h"
26 #include "BufferView.h"
27
28
29 using std::endl;
30 using SigC::slot;
31 using std::vector;
32
33 extern LyXAction lyxaction;
34
35
36 namespace {
37
38 struct prefix {
39         string p;
40         prefix(string const & s) 
41                 : p(s) {}
42         bool operator()(string const & s) const {
43                 return prefixIs(s, p);
44         }
45 };
46
47 } // end of anon namespace
48
49
50 MiniBuffer::MiniBuffer(LyXView * o, FL_Coord x, FL_Coord y,
51                        FL_Coord h, FL_Coord w)
52         : stored_(false), owner_(o), state_(spaces)
53 {
54         add(FL_NORMAL_INPUT, x, y, h, w);
55         timer.setTimeout(6000);
56         timer.timeout.connect(slot(this, &MiniBuffer::init));
57         stored_timer.setTimeout(1500);
58         stored_timer.timeout.connect(slot(this, &MiniBuffer::stored_slot));
59         deactivate();
60 }
61
62
63 void MiniBuffer::stored_slot() 
64 {
65         if (stored_) {
66                 stored_ = false;
67                 fl_set_input(the_buffer, stored_input.c_str());
68         }
69 }
70
71
72 void MiniBuffer::stored_set(string const & str) 
73 {
74         stored_input = str;
75         stored_ = true;
76         stored_timer.start();
77 }
78
79
80 int MiniBuffer::peek_event(FL_OBJECT * ob, int event, int key)
81 {
82         switch (event) {
83         case FL_KEYBOARD:
84         {
85                 char const * tmp = fl_get_input(ob);
86                 string input = tmp ? tmp : "";
87                 if (stored_) {
88                         stored_timer.stop();
89                         input = stored_input;
90                         fl_set_input(ob, input.c_str());
91                         stored_ = false;
92                 }
93                 
94                 switch (key) {
95                 case XK_Down:
96                         if (hist_iter != history_->end()) {
97                                 ++hist_iter;
98                         }
99                         if (hist_iter == history_->end()) {
100                                 // no further history
101                                 stored_set(input);
102                                 fl_set_input(ob, _("[End of history]"));
103                         } else {
104                                 fl_set_input(ob, (*hist_iter).c_str());
105                         }
106                         return 1; 
107                 case XK_Up:
108                         if (hist_iter == history_->begin()) {
109                                 // no further history
110                                 stored_set(input);
111                                 fl_set_input(ob, _("[Beginning of history]"));
112                         } else {
113                                 --hist_iter;
114                                 fl_set_input(ob, (*hist_iter).c_str());
115                         }
116                         return 1; 
117                 case 9:
118                 case XK_Tab:
119                 {
120                         // Completion handling.
121                         
122                         vector<string> comp;
123                         lyx::copy_if(completion_.begin(),
124                                      completion_.end(),
125                                      std::back_inserter(comp), prefix(input));
126
127                         if (comp.empty()) {
128                                 // No matches
129                                 string const tmp = input + _(" [no match]");
130                                 stored_set(input);
131                                 fl_set_input(ob, tmp.c_str());
132                         } else if (comp.size() == 1) {
133                                 // Perfect match
134                                 string const tmp =
135                                         comp[0] + _(" [sole completion]");
136                                 stored_set(comp[0]);
137                                 fl_set_input(ob, tmp.c_str());
138                         } else {
139                                 // More that one match
140                                 // Find maximal avaliable prefix
141                                 string const tmp = comp[0];
142                                 string test(input);
143                                 test += tmp[test.length()];
144                                 while (test.length() < tmp.length()) {
145                                         vector<string> vtmp;
146                                         lyx::copy_if(comp.begin(),
147                                                      comp.end(),
148                                                      std::back_inserter(vtmp),
149                                                      prefix(test));
150                                         if (vtmp.size() != comp.size()) {
151                                                 test.erase(test.length() - 1);
152                                                 break;
153                                         }
154                                         test += tmp[test.length()];
155                                 }
156                                 fl_set_input(ob, test.c_str());
157                                 
158                                 // How should the possible matches
159                                 // be visualized?
160                                 std::copy(comp.begin(), comp.end(),
161                                           std::ostream_iterator<string>(std::cerr, "\n"));
162                         }
163                         return 1; 
164                 }
165                 case 27:
166                 case XK_Escape:
167                         // Abort
168                         owner_->view()->focus(true);
169                         init();
170                         deactivate();
171                         //escape.emit();
172                         return 1; 
173                 case 13:
174                 case XK_Return:
175                 {
176                         // First check for match
177                         vector<string>::const_iterator cit =
178                                 std::find(completion_.begin(),
179                                           completion_.end(),
180                                           input);
181                         if (cit == completion_.end()) {
182                                 // no such func/item
183                                 stored_set(input);
184                                 string const tmp = input + _(" [no match]");
185                                 fl_set_input(ob, tmp.c_str());
186                         } else {
187                                 // Return the inputted string
188                                 deactivate();
189                                 owner_->view()->focus(true);
190                                 history_->push_back(input);
191                                 stringReady.emit(input);
192                         }
193                         return 1;
194                 }
195                 case XK_space:
196                 {
197                         // Depending on the input state spaces might not
198                         // be allowed.
199                         switch (state_) {
200                         case spaces:
201                                 return 0;
202                         case nospaces:
203                         {
204                                 stored_set(input);
205                                 string const tmp = input + _(" [no match]");
206                                 fl_set_input(ob, tmp.c_str());
207                                 return 1;
208                         }
209                         }
210                         
211                 }
212                 
213                 default:
214                         return 0;
215                 }
216         }
217         default:
218                 //lyxerr << "Unhandled minibuffer event!" << endl;
219                 break;
220         }
221         
222         return 0;
223 }
224
225
226 extern "C"
227 int C_MiniBuffer_peek_event(FL_OBJECT * ob, int event, 
228                             FL_Coord, FL_Coord,
229                             int key, void * /*xev*/)
230 {
231         MiniBuffer * mini = static_cast<MiniBuffer*>(ob->u_vdata);
232         return mini->peek_event(ob, event, key);
233 }
234
235
236 void MiniBuffer::prepare()
237 {
238         text.erase();
239         fl_set_input(the_buffer, "");
240         activate();
241         fl_set_focus_object(owner_->getForm(), the_buffer);
242 }
243
244
245 FL_OBJECT * MiniBuffer::add(int type, FL_Coord x, FL_Coord y,
246                            FL_Coord w, FL_Coord h)
247 {
248         FL_OBJECT * obj;
249         
250         the_buffer = obj = fl_add_input(type, x, y, w, h, text.c_str());
251         fl_set_object_boxtype(obj, FL_DOWN_BOX);
252         fl_set_object_resize(obj, FL_RESIZE_ALL);
253         fl_set_object_gravity(obj, SouthWestGravity, SouthEastGravity);
254         fl_set_object_color(obj, FL_MCOL, FL_MCOL);
255         fl_set_object_lsize(obj, FL_NORMAL_SIZE);
256         
257         // To intercept Up, Down, Table for history
258         fl_set_object_prehandler(obj, C_MiniBuffer_peek_event);
259         obj->u_vdata = this;
260         obj->wantkey = FL_KEY_TAB;
261
262         fl_set_input(the_buffer, text.c_str());
263         
264         return obj;
265 }
266
267
268 void MiniBuffer::message(string const & str) 
269 {
270         timer.restart();
271         string const ntext = strip(str);
272         if (!the_buffer->focus) {
273                 fl_set_input(the_buffer, ntext.c_str());
274                 text = ntext;
275         }
276 }
277
278
279 void MiniBuffer::messagePush(string const & str) 
280 {
281         text_stored = text;
282         message(str);
283 }
284
285
286 void MiniBuffer::messagePop()
287 {
288         if (!text_stored.empty()) {
289                 message(text_stored);
290                 text_stored.erase();
291         }
292 }
293
294
295 void MiniBuffer::addSet(string const & s1, string const & s2)
296 {
297         string const str = text + ' ' +  s1 + ' ' + s2;
298         message(str);
299 }
300
301
302 void MiniBuffer::getString(State spaces,
303                            vector<string> const & completion,
304                            vector<string> & history)
305 {
306         state_ = spaces;
307         completion_ = completion;
308         history_ = &history;
309         hist_iter = history_->end();
310         prepare();
311 }
312
313
314 void MiniBuffer::init()
315 {
316         // If we have focus, we don't want to change anything.
317         if (the_buffer->focus)
318                 return;
319
320         timeout.emit();
321         timer.stop();
322 }
323
324
325 void MiniBuffer::activate()
326 {
327         fl_activate_object(the_buffer);
328         fl_redraw_object(the_buffer);
329 }
330
331
332 void MiniBuffer::deactivate()
333 {
334         fl_redraw_object(the_buffer);
335         fl_deactivate_object(the_buffer);
336 }
337
338
339 void MiniBuffer::redraw() 
340 {
341         fl_redraw_object(the_buffer);
342 }