/** * \file GuiCommandBuffer.cpp * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * * \author Lars * \author Asger and Jürgen * \author John Levon * * Full author contact details are available in file CREDITS. */ #include // Qt defines a macro 'signals' that clashes with a boost namespace. // All is well if the namespace is visible first. #include "GuiView.h" #include "GuiCommandBuffer.h" #include "GuiCommandEdit.h" #include "qt_helpers.h" #include "BufferView.h" #include "Cursor.h" #include "LyXFunc.h" #include "LyXAction.h" #include "FuncRequest.h" #include "frontends/LyXView.h" #include "support/lyxalgo.h" #include "support/lstrings.h" #include "support/filetools.h" #include #include #include #include #include #include #include #include #include using std::back_inserter; using std::transform; using std::string; using std::vector; namespace lyx { namespace frontend { using support::prefixIs; using support::libFileSearch; namespace { class QTempListBox : public QListWidget { public: QTempListBox() { //setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setWindowModality(Qt::WindowModal); setWindowFlags(Qt::Popup); setAttribute(Qt::WA_DeleteOnClose); } protected: void mouseReleaseEvent(QMouseEvent * ev) { if (ev->x() < 0 || ev->y() < 0 || ev->x() > width() || ev->y() > height()) { hide(); } else { // emit signal itemPressed(currentItem()); } } void keyPressEvent(QKeyEvent * ev) { if (ev->key() == Qt::Key_Escape) { hide(); return; } else if (ev->key() == Qt::Key_Return || ev->key() == Qt::Key_Space) { // emit signal itemPressed(currentItem()); } else QListWidget::keyPressEvent(ev); } }; } // end of anon GuiCommandBuffer::GuiCommandBuffer(GuiViewBase * view) : view_(view), lv_(*view), history_pos_(history_.end()) { transform(lyxaction.func_begin(), lyxaction.func_end(), back_inserter(commands_), firster()); QPixmap qpup(toqstr(libFileSearch("images", "up", "png").absFilename())); QPixmap qpdown(toqstr(libFileSearch("images", "down", "png").absFilename())); QVBoxLayout * top = new QVBoxLayout(this); QHBoxLayout * layout = new QHBoxLayout(0); QPushButton * up = new QPushButton(qpup, "", this); up->setMaximumSize(24, 24); QPushButton * down = new QPushButton(qpdown, "", this); down->setToolTip(qt_("Next command")); down->setMaximumSize(24, 24); connect(down, SIGNAL(clicked()), this, SLOT(down())); edit_ = new GuiCommandEdit(this); edit_->setMinimumSize(edit_->sizeHint()); edit_->setFocusPolicy(Qt::ClickFocus); connect(edit_, SIGNAL(escapePressed()), this, SLOT(cancel())); connect(edit_, SIGNAL(returnPressed()), this, SLOT(dispatch())); connect(edit_, SIGNAL(tabPressed()), this, SLOT(complete())); connect(edit_, SIGNAL(upPressed()), this, SLOT(up())); connect(edit_, SIGNAL(downPressed()), this, SLOT(down())); connect(edit_, SIGNAL(hidePressed()), this, SLOT(hideParent())); layout->addWidget(up, 0); layout->addWidget(down, 0); layout->addWidget(edit_, 10); layout->setMargin(0); top->addLayout(layout); top->setMargin(0); setFocusProxy(edit_); } void GuiCommandBuffer::cancel() { view_->setFocus(); edit_->setText(QString()); } void GuiCommandBuffer::dispatch() { dispatch(fromqstr(edit_->text())); view_->setFocus(); edit_->setText(QString()); edit_->clearFocus(); } void GuiCommandBuffer::complete() { string const input = fromqstr(edit_->text()); string new_input; vector comp = completions(input, new_input); if (comp.empty() && new_input == input) { // show_info_suffix(qt_("[no match]"), input); return; } if (comp.empty()) { edit_->setText(toqstr(new_input)); // show_info_suffix(("[only completion]"), new_input + ' '); return; } edit_->setText(toqstr(new_input)); QTempListBox * list = new QTempListBox; // For some reason the scrollview's contents are larger // than the number of actual items... vector::const_iterator cit = comp.begin(); vector::const_iterator end = comp.end(); for (; cit != end; ++cit) list->addItem(toqstr(*cit)); list->resize(list->sizeHint()); QPoint const pos = edit_->mapToGlobal(QPoint(0, 0)); int const y = std::max(0, pos.y() - list->height()); list->move(pos.x(), y); connect(list, SIGNAL(itemPressed(QListWidgetItem *)), this, SLOT(complete_selected(QListWidgetItem *))); connect(list, SIGNAL(itemActivated(QListWidgetItem *)), this, SLOT(complete_selected(QListWidgetItem *))); list->show(); list->setFocus(); } void GuiCommandBuffer::complete_selected(QListWidgetItem * item) { QWidget const * widget = static_cast(sender()); const_cast(widget)->hide(); edit_->setText(item->text() + ' '); edit_->activateWindow(); edit_->setFocus(); } void GuiCommandBuffer::up() { string const input = fromqstr(edit_->text()); string const h = historyUp(); if (h.empty()) { // show_info_suffix(qt_("[Beginning of history]"), input); } else { edit_->setText(toqstr(h)); } } void GuiCommandBuffer::down() { string const input = fromqstr(edit_->text()); string const h = historyDown(); if (h.empty()) { // show_info_suffix(qt_("[End of history]"), input); } else { edit_->setText(toqstr(h)); } } void GuiCommandBuffer::hideParent() { view_->setFocus(); edit_->setText(QString()); edit_->clearFocus(); hide(); } namespace { class prefix_p { public: string p; prefix_p(string const & s) : p(s) {} bool operator()(string const & s) const { return prefixIs(s, p); } }; } // end of anon namespace string const GuiCommandBuffer::historyUp() { if (history_pos_ == history_.begin()) return string(); return *(--history_pos_); } string const GuiCommandBuffer::historyDown() { if (history_pos_ == history_.end()) return string(); if (history_pos_ + 1 == history_.end()) return string(); return *(++history_pos_); } docstring const GuiCommandBuffer::getCurrentState() const { return lv_.view()->cursor().currentState(); } void GuiCommandBuffer::hide() const { lv_.showMiniBuffer(false); } vector const GuiCommandBuffer::completions(string const & prefix, string & new_prefix) { vector comp; copy_if(commands_.begin(), commands_.end(), back_inserter(comp), prefix_p(prefix)); if (comp.empty()) { new_prefix = prefix; return comp; } if (comp.size() == 1) { new_prefix = comp[0]; return vector(); } // find maximal available prefix string const tmp = comp[0]; string test = prefix; if (tmp.length() > test.length()) test += tmp[test.length()]; while (test.length() < tmp.length()) { vector vtmp; copy_if(comp.begin(), comp.end(), back_inserter(vtmp), prefix_p(test)); if (vtmp.size() != comp.size()) { test.erase(test.length() - 1); break; } test += tmp[test.length()]; } new_prefix = test; return comp; } void GuiCommandBuffer::dispatch(string const & str) { if (str.empty()) return; history_.push_back(str); history_pos_ = history_.end(); FuncRequest func = lyxaction.lookupFunc(str); func.origin = FuncRequest::COMMANDBUFFER; lv_.dispatch(func); } } // namespace frontend } // namespace lyx #include "GuiCommandBuffer_moc.cpp"