* The MiniBuffer Class
* read minibuffer.h for more
* information.
- *
+ *
* Copyright 1995 Matthias Ettrich
- * Copyright 1995-1999 The LyX Team.
- *
+ * Copyright 1995-2001 The LyX Team.
+ *
* ###########################################################################
*/
#include <config.h>
+#include <iostream>
+
#ifdef __GNUG__
-#pragma implementation "minibuffer.h"
+#pragma implementation
#endif
+// FIXME: temporary
+#include "frontends/xforms/DropDown.h"
+#include "frontends/xforms/XFormsView.h"
+
+#include "minibuffer.h"
+
+#include "support/lyxalgo.h"
#include "support/filetools.h"
-#include "lyx_main.h"
-#include "lyxfunc.h"
-#include FORMS_H_LOCATION
-#include "minibuffer.h"
-#include "LyXView.h"
-#include "debug.h"
+#include "support/lstrings.h"
+#include "frontends/LyXView.h"
#include "gettext.h"
+#include "LyXAction.h"
+#include "BufferView.h"
+#include "frontends/Timeout.h"
+
+#include <boost/bind.hpp>
+
+#include <cctype>
+
+#ifndef CXX_GLOBAL_CSTD
+using std::isprint;
+#endif
+
+using std::vector;
+using std::back_inserter;
+using std::find;
-extern bool keyseqUncomplete();
-extern string keyseqOptions(int l=190);
-extern string keyseqStr(int l=190);
extern LyXAction lyxaction;
-void MiniBuffer::TimerCB(FL_OBJECT *, long tmp)
+
+namespace {
+
+struct prefix {
+ string p;
+ prefix(string const & s)
+ : p(s) {}
+ bool operator()(string const & s) const {
+ return prefixIs(s, p);
+ }
+};
+
+} // end of anon namespace
+
+
+MiniBuffer::MiniBuffer(LyXView * o, FL_Coord x, FL_Coord y,
+ FL_Coord h, FL_Coord w)
+ : stored_(false), owner_(o), state_(spaces)
{
- MiniBuffer *obj= (MiniBuffer*)tmp;
- obj->Init();
+ add(FL_NORMAL_INPUT, x, y, h, w);
+
+ timer = new Timeout(6000);
+ timer->timeout.connect(boost::bind(&MiniBuffer::init, this));
+
+ stored_timer = new Timeout(1500);
+ stored_timer->timeout.connect(boost::bind(&MiniBuffer::stored_slot, this));
+ deactivate();
}
-extern "C" void C_MiniBuffer_TimerCB(FL_OBJECT *ob, long data)
+
+// thanks for nothing, xforms (recursive creation not allowed)
+void MiniBuffer::dd_init()
{
- MiniBuffer::TimerCB(ob, data);
+ dropdown_ = new DropDown(owner_, the_buffer);
+ dropdown_->result.connect(boost::bind(&MiniBuffer::set_complete_input, this, _1));
+ dropdown_->keypress.connect(boost::bind(&MiniBuffer::append_char, this, _1));
}
-void MiniBuffer::ExecutingCB(FL_OBJECT *ob, long)
+
+MiniBuffer::~MiniBuffer()
{
- MiniBuffer *obj = (MiniBuffer*)ob->u_vdata;
- lyxerr.debug() << "Getting ready to execute: " << obj->cur_cmd << endl;
- fl_set_focus_object(obj->owner->getForm(),
- obj->owner->currentView()->getWorkArea());
- if (obj->cur_cmd.empty()) {
- obj->Init();
- return ;
- }
- obj->Set(_("Executing:"), obj->cur_cmd);
- obj->addHistory(obj->cur_cmd);
-
- // Split command into function and argument
- string arg = obj->cur_cmd;
- string function;
- if (contains(arg, " ")) {
- arg = split(arg, function, ' ');
- function = strip(function);
- } else {
- function = arg;
- arg.clear();
- }
- lyxerr.debug() << "Function: " << function
- << "\nArg : " << arg << endl;
+ delete timer;
+ delete stored_timer;
+ delete dropdown_;
+}
- // Dispatch only returns requested data for a few commands (ale)
- string res=obj->owner->getLyXFunc()->Dispatch(function.c_str(),
- arg.c_str());
- lyxerr.debug() << "Minibuffer Res: " << res << endl;
- obj->shows_no_match = false;
- return ;
+void MiniBuffer::stored_slot()
+{
+ if (stored_) {
+ stored_ = false;
+ set_input(stored_input);
+ }
}
-extern "C" void C_MiniBuffer_ExecutingCB(FL_OBJECT *ob, long data)
+
+void MiniBuffer::stored_set(string const & str)
{
- MiniBuffer::TimerCB(ob, data);
+ stored_input = str;
+ stored_ = true;
+ stored_timer->start();
}
-// This is not as dirty as it seems, the hidden buttons removed by this
-// function were just kludges for an uncomplete keyboard callback (ale)
-int MiniBuffer::peek_event(FL_OBJECT *ob, int event, FL_Coord, FL_Coord,
- int key, void */*xev*/)
+
+int MiniBuffer::peek_event(FL_OBJECT * ob, int event, int key)
{
- MiniBuffer *mini = (MiniBuffer*)ob->u_vdata;
-
- if (event==FL_KEYBOARD){
+ switch (event) {
+ case FL_UNFOCUS:
+ deactivate();
+ break;
+ case FL_KEYBOARD:
+ {
+ char const * tmp = fl_get_input(ob);
+ string input = tmp ? tmp : "";
+ if (stored_) {
+ stored_timer->stop();
+ input = stored_input;
+ set_input(input);
+ stored_ = false;
+ }
+
switch (key) {
case XK_Down:
- mini->history_idx++;
- if (!mini->getHistory().empty()) {
- fl_set_input(ob, mini->getHistory().c_str());
- } else
- mini->history_idx--;
- return 1;
+ if (hist_iter != history_->end()) {
+ ++hist_iter;
+ }
+ if (hist_iter == history_->end()) {
+ // no further history
+ stored_set(input);
+ set_input(_("[End of history]"));
+ } else {
+ set_input((*hist_iter));
+ }
+ return 1;
case XK_Up:
- if (mini->history_idx > 0) mini->history_idx--;
- fl_set_input(ob, mini->getHistory().c_str());
- return 1;
+ if (hist_iter == history_->begin()) {
+ // no further history
+ stored_set(input);
+ set_input(_("[Beginning of history]"));
+ } else {
+ --hist_iter;
+ set_input((*hist_iter));
+ }
+ return 1;
case 9:
case XK_Tab:
{
- // complete or increment the command
- const char *s = lyxaction.getApproxFuncName(fl_get_input(ob));
- if (s && s[0])
- fl_set_input(ob, s);
- return 1;
+ // Completion handling.
+
+ vector<string> comp;
+ lyx::copy_if(completion_.begin(),
+ completion_.end(),
+ back_inserter(comp), prefix(input));
+
+ if (comp.empty()) {
+ // No matches
+ string const tmp = input + _(" [no match]");
+ stored_set(input);
+ set_input(tmp);
+ } else if (comp.size() == 1) {
+ // Perfect match
+ string const tmp =
+ comp[0] + _(" [sole completion]");
+ stored_set(comp[0] + " ");
+ set_input(tmp);
+ } else {
+ // More that one match
+ // Find maximal avaliable prefix
+ string const tmp = comp[0];
+ string test(input);
+ if (tmp.length() > test.length())
+ test += tmp[test.length()];
+ while (test.length() < tmp.length()) {
+ vector<string> vtmp;
+ lyx::copy_if(comp.begin(),
+ comp.end(),
+ back_inserter(vtmp),
+ prefix(test));
+ if (vtmp.size() != comp.size()) {
+ test.erase(test.length() - 1);
+ break;
+ }
+ test += tmp[test.length()];
+ }
+ set_input(test);
+
+ int x,y,w,h;
+ fl_get_wingeometry(fl_get_real_object_window(the_buffer),
+ &x, &y, &w, &h);
+
+ // asynchronous completion
+ int const air = the_buffer->x;
+ x += air;
+ y += h - (the_buffer->h + air);
+ w = the_buffer->w;
+ dropdown_->select(comp, x, y, w);
+ }
+ return 1;
}
case 27:
case XK_Escape:
// Abort
- fl_set_focus_object(mini->owner->getForm(),
- mini->owner->currentView()->getWorkArea());
- mini->Init();
- return 1;
+ owner_->view()->focus(true);
+ init();
+ deactivate();
+ //escape.emit();
+ return 1;
case 13:
case XK_Return:
- // Execute a command.
- mini->cur_cmd = string(fl_get_input(ob));
- ExecutingCB(ob, 0);
+ {
+#if 0
+ // This will go in again in a little while
+ // we need to be able to declare what types
+ // of argumetns LFUN's should have first. (Lgb)
+ // First check for match
+ vector<string>::const_iterator cit =
+ find(completion_.begin(),
+ completion_.end(),
+ input);
+ if (cit == completion_.end()) {
+ // no such func/item
+ stored_set(input);
+ string const tmp = input + _(" [no match]");
+ set_input(tmp);
+ } else {
+#endif
+ // Return the inputted string
+ deactivate();
+ owner_->view()->focus(true);
+ if (!input.empty()) {
+ history_->push_back(input);
+ }
+ stringReady(input);
+# if 0
+ }
+#endif
return 1;
+ }
+ case XK_space:
+ {
+ // Depending on the input state spaces might not
+ // be allowed.
+ switch (state_) {
+ case spaces:
+ return 0;
+ case nospaces:
+ {
+ stored_set(input);
+ string const tmp = input + _(" [no match]");
+ set_input(tmp);
+ return 1;
+ }
+ }
+
+ }
+
default:
return 0;
}
}
+ default:
+ //lyxerr << "Unhandled minibuffer event!" << endl;
+ break;
+ }
+
return 0;
}
-extern "C" int C_MiniBuffer_peek_event(FL_OBJECT *ob, int event,
- FL_Coord, FL_Coord,
- int key, void *xev)
-{
- return MiniBuffer::peek_event(ob,event,0,0,key,xev);
+
+extern "C" {
+
+ static
+ int C_MiniBuffer_peek_event(FL_OBJECT * ob, int event,
+ FL_Coord, FL_Coord,
+ int key, void * /*xev*/)
+ {
+ MiniBuffer * mini = static_cast<MiniBuffer*>(ob->u_vdata);
+ return mini->peek_event(ob, event, key);
+ }
+
}
-void MiniBuffer::ExecCommand()
+void MiniBuffer::prepare()
{
- text.clear();
- fl_set_input(the_buffer, "");
- fl_set_focus_object(owner->getForm(),the_buffer);
+ text.erase();
+ set_input("");
+ activate();
+ fl_set_focus_object(static_cast<XFormsView *>(owner_)->getForm(),
+ the_buffer);
}
-FL_OBJECT *MiniBuffer::add(int type, FL_Coord x, FL_Coord y,
+FL_OBJECT * MiniBuffer::add(int type, FL_Coord x, FL_Coord y,
FL_Coord w, FL_Coord h)
{
- FL_OBJECT *obj;
-
- the_buffer = obj = fl_add_input(type,x,y,w,h,text.c_str());
- fl_set_object_boxtype(obj,FL_DOWN_BOX);
- fl_set_object_resize(obj, FL_RESIZE_ALL);
- fl_set_object_gravity(obj, SouthWestGravity, SouthEastGravity);
- fl_set_object_color(obj,FL_MCOL,FL_MCOL);
- fl_set_object_lsize(obj,FL_NORMAL_SIZE);
- fl_set_object_callback(obj,C_MiniBuffer_ExecutingCB, 0);
+ FL_OBJECT * obj;
+
+ the_buffer = obj = fl_add_input(type, x, y, w, h, text.c_str());
+ fl_set_object_boxtype(obj, FL_DOWN_BOX);
+ fl_set_object_resize(obj, FL_RESIZE_ALL);
+ fl_set_object_gravity(obj, SouthWestGravity, SouthEastGravity);
+ fl_set_object_color(obj, FL_MCOL, FL_MCOL);
+ fl_set_object_lsize(obj, FL_NORMAL_SIZE);
// To intercept Up, Down, Table for history
- fl_set_object_prehandler(obj, C_MiniBuffer_peek_event);
- obj->u_vdata = (void*)this;
- obj->wantkey = FL_KEY_TAB;
-
- // timer
- timer = fl_add_timer(FL_HIDDEN_TIMER, 0,0,0,0, "Timer");
- fl_set_object_callback(timer, C_MiniBuffer_TimerCB, (long)this);
- fl_set_input(the_buffer, text.c_str());
+ fl_set_object_prehandler(obj, C_MiniBuffer_peek_event);
+ obj->u_vdata = this;
+ obj->wantkey = FL_KEY_TAB;
+
+ set_input(text);
return obj;
}
-// Added optional arg `delay_secs', defaults to 4.
-//When 0, no timeout is done. RVDK_PATCH_5
-void MiniBuffer::Set(string const& s1, string const& s2,
- string const& s3, int delay_secs)
+void MiniBuffer::message(string const & str)
{
- setTimer(delay_secs);
-
- string ntext = strip(s1 + ' ' + s2 + ' ' + s3);
-
+ timer->restart();
+ string const ntext = strip(str);
if (!the_buffer->focus) {
- fl_set_input(the_buffer, ntext.c_str());
- XFlush(fl_display);
+ set_input(ntext);
text = ntext;
}
}
-void MiniBuffer::Init()
+void MiniBuffer::messagePush(string const & str)
{
- // If we have focus, we don't want to change anything.
- if (the_buffer->focus)
- return;
+ text_stored = text;
+ message(str);
+}
- // When meta-fake key is pressed, show the key sequence so far + "M-".
- if (owner->getLyXFunc()->wasMetaKey()) {
- text = owner->getLyXFunc()->keyseqStr();
- text += " M-";
- }
- // Else, when a non-complete key sequence is pressed,
- // show the available options.
- else if (owner->getLyXFunc()->keyseqUncomplete())
- text = owner->getLyXFunc()->keyseqOptions();
-
- // Else, show the buffer state.
- else if (owner->currentView()->available()) {
- string nicename =
- MakeDisplayPath(owner->currentBuffer()->
- getFileName());
- // Should we do this instead? (kindo like emacs)
- // leaves more room for other information
- text = "LyX: ";
- text += nicename;
- if (owner->currentBuffer()->lyxvc.inUse()) {
- text += " [RCS:";
- text += owner->currentBuffer()->lyxvc.getVersion();
- text += ' ';
- text += owner->currentBuffer()->lyxvc.getLocker();
- if (owner->currentBuffer()->isReadonly())
- text += " (RO)";
- text += ']';
- } else if (owner->currentBuffer()->isReadonly())
- text += " [RO]";
- if (!owner->currentBuffer()->isLyxClean())
- text += _(" (Changed)");
- } else {
- if (text != _("Welcome to LyX!")) // this is a hack
- text = _("* No document open *");
+void MiniBuffer::messagePop()
+{
+ if (!text_stored.empty()) {
+ message(text_stored);
+ text_stored.erase();
}
-
+}
- fl_set_input(the_buffer, text.c_str());
- setTimer(0);
- XFlush(fl_display);
+
+void MiniBuffer::addSet(string const & s1, string const & s2)
+{
+ string const str = text + ' ' + s1 + ' ' + s2;
+ message(str);
}
-// allows to store and reset the contents one time. Usefull for
-// status messages like "load font" (Matthias)
-void MiniBuffer::Store()
+void MiniBuffer::getString(State spaces,
+ vector<string> const & completion,
+ vector<string> & history)
{
- text_stored = fl_get_input(the_buffer);
+ state_ = spaces;
+ completion_ = completion;
+ history_ = &history;
+ hist_iter = history_->end();
+ prepare();
}
-void MiniBuffer::Reset()
+void MiniBuffer::init()
{
- if (!text_stored.empty()){
- Set(text_stored);
- text_stored.clear();
- }
+ // If we have focus, we don't want to change anything.
+ if (the_buffer->focus)
+ return;
+
+ timeout();
+ timer->stop();
}
-void MiniBuffer::Activate()
+
+void MiniBuffer::activate()
{
fl_activate_object(the_buffer);
- fl_redraw_object(the_buffer);
+ redraw();
}
-void MiniBuffer::Deactivate()
+
+void MiniBuffer::deactivate()
{
+ redraw();
fl_deactivate_object(the_buffer);
+ XFlush(fl_display);
+}
+
+
+void MiniBuffer::redraw()
+{
+ fl_redraw_object(the_buffer);
+ XFlush(fl_display);
+}
+
+
+void MiniBuffer::set_complete_input(string const & str)
+{
+ if (!str.empty()) {
+ // add a space so the user can type
+ // an argument immediately
+ set_input(str + " ");
+ }
}
+void MiniBuffer::append_char(char c)
+{
+ if (!c || !isprint(c))
+ return;
+
+ char const * tmp = fl_get_input(the_buffer);
+ string str = tmp ? tmp : "";
+
+ str += c;
+
+ fl_set_input(the_buffer, str.c_str());
+}
+
+
+void MiniBuffer::set_input(string const & str)
+{
+ fl_set_input(the_buffer, str.c_str());
+ XFlush(fl_display);
+}