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