mirror of
https://git.lyx.org/repos/lyx.git
synced 2025-01-21 23:09:40 +00:00
* completion infrastructure
* completion support for mathed * experimental completion support for text git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@23104 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
parent
a51525c416
commit
1bf7b59d60
@ -717,6 +717,7 @@ src_frontends_qt4_header_files = Split('''
|
||||
GuiClipboard.h
|
||||
GuiCommandBuffer.h
|
||||
GuiCommandEdit.h
|
||||
GuiCompleter.h
|
||||
GuiDelimiter.h
|
||||
GuiDialog.h
|
||||
GuiDocument.h
|
||||
@ -807,6 +808,7 @@ src_frontends_qt4_files = Split('''
|
||||
GuiClipboard.cpp
|
||||
GuiCommandBuffer.cpp
|
||||
GuiCommandEdit.cpp
|
||||
GuiCompleter.cpp
|
||||
GuiDelimiter.cpp
|
||||
GuiDialog.cpp
|
||||
GuiDocument.cpp
|
||||
|
@ -2664,4 +2664,10 @@ void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Buffer::registerWord(docstring const & word)
|
||||
{
|
||||
words_.insert(word);
|
||||
}
|
||||
|
||||
} // namespace lyx
|
||||
|
@ -367,6 +367,7 @@ public:
|
||||
void updateMacroInstances() const;
|
||||
|
||||
typedef std::set<docstring> MacroNameSet;
|
||||
|
||||
/// List macro names of this buffer. the parent and the children
|
||||
void listMacroNames(MacroNameSet & macros) const;
|
||||
/// Write out all macros somewhere defined in the parent,
|
||||
@ -453,6 +454,11 @@ public:
|
||||
///
|
||||
std::vector<Format const *> exportableFormats(bool only_viewable) const;
|
||||
|
||||
/// Register word for completion word list.
|
||||
void registerWord(docstring const & word);
|
||||
///
|
||||
std::set<docstring> const & registeredWords() const { return words_; }
|
||||
|
||||
private:
|
||||
/// search for macro in local (buffer) table or in children
|
||||
MacroData const * getBufferMacro(docstring const & name,
|
||||
@ -504,6 +510,8 @@ private:
|
||||
//Signal setBusy(bool) = 0;
|
||||
/// Reset autosave timers for all users.
|
||||
Signal resetAutosaveTimers_;
|
||||
|
||||
std::set<docstring> words_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -238,6 +238,13 @@ struct BufferView::Private
|
||||
///
|
||||
vector<int> par_height_;
|
||||
|
||||
///
|
||||
DocIterator inlineCompletionPos;
|
||||
///
|
||||
docstring inlineCompletion;
|
||||
///
|
||||
size_t inlineCompletionUniqueChars;
|
||||
|
||||
/// keyboard mapping object.
|
||||
Intl intl_;
|
||||
|
||||
@ -778,6 +785,10 @@ void BufferView::showCursor(DocIterator const & dit)
|
||||
return;
|
||||
}
|
||||
|
||||
// fix inline completion position
|
||||
if (d->inlineCompletionPos.fixIfBroken())
|
||||
d->inlineCompletionPos = DocIterator();
|
||||
|
||||
tm.redoParagraph(bot_pit);
|
||||
ParagraphMetrics const & pm = tm.parMetrics(bot_pit);
|
||||
int offset = coordOffset(dit, dit.boundary()).y_;
|
||||
@ -1753,6 +1764,10 @@ bool BufferView::singleParUpdate()
|
||||
TextMetrics & tm = textMetrics(&buftext);
|
||||
int old_height = tm.parMetrics(bottom_pit).height();
|
||||
|
||||
// make sure inline completion pointer is ok
|
||||
if (d->inlineCompletionPos.fixIfBroken())
|
||||
d->inlineCompletionPos = DocIterator();
|
||||
|
||||
// In Single Paragraph mode, rebreak only
|
||||
// the (main text, not inset!) paragraph containing the cursor.
|
||||
// (if this paragraph contains insets etc., rebreaking will
|
||||
@ -1789,6 +1804,10 @@ void BufferView::updateMetrics()
|
||||
|
||||
TextMetrics & tm = textMetrics(&buftext);
|
||||
|
||||
// make sure inline completion pointer is ok
|
||||
if (d->inlineCompletionPos.fixIfBroken())
|
||||
d->inlineCompletionPos = DocIterator();
|
||||
|
||||
// Rebreak anchor paragraph.
|
||||
tm.redoParagraph(d->anchor_pit_);
|
||||
ParagraphMetrics & anchor_pm = tm.par_metrics_[d->anchor_pit_];
|
||||
@ -2152,4 +2171,31 @@ void BufferView::insertPlaintextFile(FileName const & f, bool asParagraph)
|
||||
buffer_.changed();
|
||||
}
|
||||
|
||||
|
||||
docstring const & BufferView::inlineCompletion() const
|
||||
{
|
||||
return d->inlineCompletion;
|
||||
}
|
||||
|
||||
|
||||
size_t const & BufferView::inlineCompletionUniqueChars() const
|
||||
{
|
||||
return d->inlineCompletionUniqueChars;
|
||||
}
|
||||
|
||||
|
||||
DocIterator const & BufferView::inlineCompletionPos() const
|
||||
{
|
||||
return d->inlineCompletionPos;
|
||||
}
|
||||
|
||||
|
||||
void BufferView::setInlineCompletion(DocIterator const & pos,
|
||||
docstring const & completion, size_t uniqueChars)
|
||||
{
|
||||
d->inlineCompletionPos = pos;
|
||||
d->inlineCompletion = completion;
|
||||
d->inlineCompletionUniqueChars = min(completion.size(), uniqueChars);
|
||||
}
|
||||
|
||||
} // namespace lyx
|
||||
|
@ -162,6 +162,15 @@ public:
|
||||
/// return the pixel height of the document view.
|
||||
int workHeight() const;
|
||||
|
||||
/// return the inline completion postfix.
|
||||
docstring const & inlineCompletion() const;
|
||||
/// return the number of unique characters in the inline completion.
|
||||
size_t const & inlineCompletionUniqueChars() const;
|
||||
/// return the position in the buffer of the inline completion postfix.
|
||||
DocIterator const & inlineCompletionPos() const;
|
||||
/// set the inline completion postfix and its position in the buffer.
|
||||
void setInlineCompletion(DocIterator const & pos, docstring const & completion,
|
||||
size_t uniqueChars = 0);
|
||||
|
||||
/// translate and insert a character, using the correct keymap.
|
||||
void translateAndInsert(char_type c, Text * t, Cursor & cur);
|
||||
|
@ -103,6 +103,10 @@ ColorSet::ColorSet()
|
||||
{ Color_foreground, N_("text"), "foreground", "black", "foreground" },
|
||||
{ Color_selection, N_("selection"), "selection", "LightBlue", "selection" },
|
||||
{ Color_latex, N_("LaTeX text"), "latex", "DarkRed", "latex" },
|
||||
{ Color_inlinecompletion, N_("inline completion"),
|
||||
"inlinecompletion", "grey70", "inlinecompletion" },
|
||||
{ Color_nonunique_inlinecompletion, N_("non-unique inline completion"),
|
||||
"nonuniqueinlinecompletion", "grey80", "nonuniqueinlinecompletion" },
|
||||
{ Color_preview, N_("previewed snippet"), "preview", "black", "preview" },
|
||||
{ Color_note, N_("note"), "note", "yellow", "note" },
|
||||
{ Color_notebg, N_("note background"), "notebg", "yellow", "notebg" },
|
||||
|
@ -48,6 +48,10 @@ enum ColorCode
|
||||
Color_latex,
|
||||
/// The color used for previews
|
||||
Color_preview,
|
||||
/// Inline completion color
|
||||
Color_inlinecompletion,
|
||||
/// Inline completion color for the non-unique part
|
||||
Color_nonunique_inlinecompletion,
|
||||
|
||||
/// Text color for notes
|
||||
Color_note,
|
||||
|
@ -1292,6 +1292,12 @@ InsetMathUnknown * Cursor::activeMacro()
|
||||
}
|
||||
|
||||
|
||||
InsetMathUnknown const * Cursor::activeMacro() const
|
||||
{
|
||||
return inMacroMode() ? prevAtom().nucleus()->asUnknownInset() : 0;
|
||||
}
|
||||
|
||||
|
||||
void Cursor::pullArg()
|
||||
{
|
||||
// FIXME: Look here
|
||||
|
@ -350,6 +350,8 @@ public:
|
||||
bool inMacroMode() const;
|
||||
/// get access to the macro we are currently typing
|
||||
InsetMathUnknown * activeMacro();
|
||||
/// get access to the macro we are currently typing
|
||||
InsetMathUnknown const * activeMacro() const;
|
||||
|
||||
/// replace selected stuff with at, placing the former
|
||||
// selection in given cell of atom
|
||||
|
@ -428,6 +428,9 @@ void DocIterator::updateInsets(Inset * inset)
|
||||
|
||||
bool DocIterator::fixIfBroken()
|
||||
{
|
||||
if (empty())
|
||||
return false;
|
||||
|
||||
// Go through the slice stack from the bottom.
|
||||
// Check that all coordinates (idx, pit, pos) are correct and
|
||||
// that the inset is the one which is claimed to be there
|
||||
|
@ -1467,6 +1467,30 @@ void LyXAction::init()
|
||||
* \endvar
|
||||
*/
|
||||
{ LFUN_STATISTICS, "statistics", ReadOnly, System },
|
||||
/*!
|
||||
* \var lyx::kb_action lyx::LFUN_COMPLETION_INLINE
|
||||
* \li Action: Show the inline completion at the cursor position.
|
||||
* \li Syntax: completion-inline
|
||||
* \li Origin: sts, Feb 19 2008
|
||||
* \endvar
|
||||
*/
|
||||
{ LFUN_COMPLETION_INLINE, "completion-inline", ReadOnly | NoUpdate, Edit },
|
||||
/*!
|
||||
* \var lyx::kb_action lyx::LFUN_COMPLETION_POPUP
|
||||
* \li Action: Show the completion popup at the cursor position.
|
||||
* \li Syntax: completion-popup
|
||||
* \li Origin: sts, Feb 19 2008
|
||||
* \endvar
|
||||
*/
|
||||
{ LFUN_COMPLETION_POPUP, "completion-popup", ReadOnly | NoUpdate, Edit },
|
||||
/*!
|
||||
* \var lyx::kb_action lyx::LFUN_COMPLETION_COMPLETE
|
||||
* \li Action: Try to complete the word or command at the cursor position.
|
||||
* \li Syntax: complete
|
||||
* \li Origin: sts, Feb 19 2007
|
||||
* \endvar
|
||||
*/
|
||||
{ LFUN_COMPLETION_COMPLETE, "complete", SingleParUpdate, Edit },
|
||||
|
||||
{ LFUN_NOACTION, "", Noop, Hidden }
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
||||
|
@ -366,9 +366,17 @@ void LyXFunc::processKeySym(KeySymbol const & keysym, KeyModifier state)
|
||||
dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
|
||||
FuncRequest::KEYBOARD));
|
||||
LYXERR(Debug::KEY, "SelfInsert arg[`" << to_utf8(arg) << "']");
|
||||
lyx_view_->updateCompletion(true, true);
|
||||
}
|
||||
} else {
|
||||
dispatch(func);
|
||||
if (func.action == LFUN_CHAR_DELETE_BACKWARD)
|
||||
// backspace is not a self-insertion. But it
|
||||
// still should not hide the completion popup.
|
||||
// FIXME: more clever way to detect those movements
|
||||
lyx_view_->updateCompletion(false, true);
|
||||
else
|
||||
lyx_view_->updateCompletion(false, false);
|
||||
}
|
||||
|
||||
lyx_view_->restartCursor();
|
||||
@ -459,6 +467,14 @@ FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
|
||||
enable = false;
|
||||
break;
|
||||
|
||||
case LFUN_COMPLETION_POPUP:
|
||||
case LFUN_COMPLETION_INLINE:
|
||||
case LFUN_COMPLETION_COMPLETE:
|
||||
if (lyx_view_)
|
||||
return lyx_view_->getStatus(cmd);
|
||||
enable = false;
|
||||
break;
|
||||
|
||||
case LFUN_BUFFER_TOGGLE_READ_ONLY:
|
||||
flag.setOnOff(buf->isReadonly());
|
||||
break;
|
||||
@ -1897,6 +1913,14 @@ void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
|
||||
case LyXRC::RC_BIBTEX_COMMAND:
|
||||
case LyXRC::RC_BINDFILE:
|
||||
case LyXRC::RC_CHECKLASTFILES:
|
||||
case LyXRC::RC_COMPLETION_INLINE_DELAY:
|
||||
case LyXRC::RC_COMPLETION_INLINE_MATH:
|
||||
case LyXRC::RC_COMPLETION_INLINE_TEXT:
|
||||
case LyXRC::RC_COMPLETION_INLINE_DOTS:
|
||||
case LyXRC::RC_COMPLETION_POPUP_DELAY:
|
||||
case LyXRC::RC_COMPLETION_POPUP_MATH:
|
||||
case LyXRC::RC_COMPLETION_POPUP_TEXT:
|
||||
case LyXRC::RC_COMPLETION_POPUP_AFTER_COMPLETE:
|
||||
case LyXRC::RC_USELASTFILEPOS:
|
||||
case LyXRC::RC_LOADSESSION:
|
||||
case LyXRC::RC_CHKTEX_COMMAND:
|
||||
|
167
src/LyXRC.cpp
167
src/LyXRC.cpp
@ -63,6 +63,14 @@ keyword_item lyxrcTags[] = {
|
||||
{ "\\bind_file", LyXRC::RC_BINDFILE },
|
||||
{ "\\check_lastfiles", LyXRC::RC_CHECKLASTFILES },
|
||||
{ "\\chktex_command", LyXRC::RC_CHKTEX_COMMAND },
|
||||
{ "\\completion_inline_delay", LyXRC::RC_COMPLETION_INLINE_DELAY },
|
||||
{ "\\completion_inline_math", LyXRC::RC_COMPLETION_INLINE_MATH },
|
||||
{ "\\completion_inline_text", LyXRC::RC_COMPLETION_INLINE_TEXT },
|
||||
{ "\\completion_inline_dots", LyXRC::RC_COMPLETION_INLINE_DOTS },
|
||||
{ "\\completion_popup_delay", LyXRC::RC_COMPLETION_POPUP_DELAY },
|
||||
{ "\\completion_popup_math", LyXRC::RC_COMPLETION_POPUP_MATH },
|
||||
{ "\\completion_popup_text", LyXRC::RC_COMPLETION_POPUP_TEXT },
|
||||
{ "\\completion_popup_after_complete", LyXRC::RC_COMPLETION_POPUP_AFTER_COMPLETE },
|
||||
{ "\\converter", LyXRC::RC_CONVERTER },
|
||||
{ "\\converter_cache_maxage", LyXRC::RC_CONVERTER_CACHE_MAXAGE },
|
||||
{ "\\copier", LyXRC::RC_COPIER },
|
||||
@ -280,6 +288,9 @@ void LyXRC::setDefaults() {
|
||||
use_tooltip = true;
|
||||
use_pixmap_cache = false;
|
||||
converter_cache_maxage = 6 * 30 * 24 * 3600; // 6 months
|
||||
user_name = to_utf8(support::user_name());
|
||||
user_email = to_utf8(support::user_email());
|
||||
|
||||
// Fullscreen settings
|
||||
full_screen_limit = false;
|
||||
full_screen_toolbars = true;
|
||||
@ -287,9 +298,14 @@ void LyXRC::setDefaults() {
|
||||
full_screen_scrollbar = true;
|
||||
full_screen_width = 700;
|
||||
|
||||
user_name = to_utf8(support::user_name());
|
||||
|
||||
user_email = to_utf8(support::user_email());
|
||||
completion_popup_math = true;
|
||||
completion_popup_text = false;
|
||||
completion_popup_delay = 2.0;
|
||||
completion_popup_after_complete = true;
|
||||
completion_inline_math = true;
|
||||
completion_inline_text = false;
|
||||
completion_inline_dots = -1;
|
||||
completion_inline_delay = 0.2;
|
||||
}
|
||||
|
||||
|
||||
@ -755,6 +771,54 @@ int LyXRC::read(Lexer & lexrc)
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_COMPLETION_INLINE_DELAY:
|
||||
if (lexrc.next()) {
|
||||
completion_inline_delay = lexrc.getFloat();
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_COMPLETION_INLINE_MATH:
|
||||
if (lexrc.next()) {
|
||||
completion_inline_math = lexrc.getBool();
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_COMPLETION_INLINE_TEXT:
|
||||
if (lexrc.next()) {
|
||||
completion_inline_text = lexrc.getBool();
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_COMPLETION_INLINE_DOTS:
|
||||
if (lexrc.next()) {
|
||||
completion_inline_dots = lexrc.getInteger();
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_COMPLETION_POPUP_DELAY:
|
||||
if (lexrc.next()) {
|
||||
completion_popup_delay = lexrc.getFloat();
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_COMPLETION_POPUP_MATH:
|
||||
if (lexrc.next()) {
|
||||
completion_popup_math = lexrc.getBool();
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_COMPLETION_POPUP_TEXT:
|
||||
if (lexrc.next()) {
|
||||
completion_popup_text = lexrc.getBool();
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_COMPLETION_POPUP_AFTER_COMPLETE:
|
||||
if (lexrc.next()) {
|
||||
completion_popup_after_complete = lexrc.getBool();
|
||||
}
|
||||
break;
|
||||
|
||||
case RC_NUMLASTFILES:
|
||||
if (lexrc.next()) {
|
||||
num_lastfiles = lexrc.getInteger();
|
||||
@ -2041,6 +2105,69 @@ void LyXRC::write(ostream & os, bool ignore_system_lyxrc, string const & name) c
|
||||
}
|
||||
if (tag != RC_LAST)
|
||||
break;
|
||||
case RC_COMPLETION_INLINE_DELAY:
|
||||
if (ignore_system_lyxrc ||
|
||||
completion_inline_delay != system_lyxrc.completion_inline_delay) {
|
||||
os << "\\completion_inline_delay " << completion_inline_delay << '\n';
|
||||
}
|
||||
if (tag != RC_LAST)
|
||||
break;
|
||||
case RC_COMPLETION_INLINE_MATH:
|
||||
if (ignore_system_lyxrc ||
|
||||
completion_inline_math != system_lyxrc.completion_inline_math) {
|
||||
os << "\\completion_inline_math "
|
||||
<< convert<string>(completion_inline_math) << '\n';
|
||||
}
|
||||
if (tag != RC_LAST)
|
||||
break;
|
||||
case RC_COMPLETION_INLINE_TEXT:
|
||||
if (ignore_system_lyxrc ||
|
||||
completion_inline_text != system_lyxrc.completion_inline_text) {
|
||||
os << "\\completion_inline_text "
|
||||
<< convert<string>(completion_inline_text) << '\n';
|
||||
}
|
||||
if (tag != RC_LAST)
|
||||
break;
|
||||
case RC_COMPLETION_INLINE_DOTS:
|
||||
if (ignore_system_lyxrc ||
|
||||
completion_inline_dots != system_lyxrc.completion_inline_dots) {
|
||||
os << "\\completion_inline_dots "
|
||||
<< convert<string>(completion_inline_dots) << '\n';
|
||||
}
|
||||
if (tag != RC_LAST)
|
||||
break;
|
||||
case RC_COMPLETION_POPUP_DELAY:
|
||||
if (ignore_system_lyxrc ||
|
||||
completion_popup_delay != system_lyxrc.completion_popup_delay) {
|
||||
os << "\\completion_popup_delay " << completion_popup_delay << '\n';
|
||||
}
|
||||
if (tag != RC_LAST)
|
||||
break;
|
||||
case RC_COMPLETION_POPUP_MATH:
|
||||
if (ignore_system_lyxrc ||
|
||||
completion_popup_math != system_lyxrc.completion_popup_math) {
|
||||
os << "\\completion_popup_math "
|
||||
<< convert<string>(completion_popup_math) << '\n';
|
||||
}
|
||||
if (tag != RC_LAST)
|
||||
break;
|
||||
case RC_COMPLETION_POPUP_TEXT:
|
||||
if (ignore_system_lyxrc ||
|
||||
completion_popup_text != system_lyxrc.completion_popup_text) {
|
||||
os << "\\completion_popup_text "
|
||||
<< convert<string>(completion_popup_text) << '\n';
|
||||
}
|
||||
if (tag != RC_LAST)
|
||||
break;
|
||||
case RC_COMPLETION_POPUP_AFTER_COMPLETE:
|
||||
if (ignore_system_lyxrc ||
|
||||
completion_popup_after_complete
|
||||
!= system_lyxrc.completion_popup_after_complete) {
|
||||
os << "\\completion_popup_after_complete "
|
||||
<< convert<string>(completion_popup_after_complete) << '\n';
|
||||
}
|
||||
if (tag != RC_LAST)
|
||||
break;
|
||||
case RC_NUMLASTFILES:
|
||||
if (ignore_system_lyxrc ||
|
||||
num_lastfiles != system_lyxrc.num_lastfiles) {
|
||||
@ -2637,10 +2764,42 @@ string const LyXRC::getDescription(LyXRCTags tag)
|
||||
break;
|
||||
|
||||
case RC_MOUSE_WHEEL_SPEED:
|
||||
str = bformat(_("The scrolling speed of the mouse wheel. "),
|
||||
str = bformat(_("The scrolling speed of the mouse wheel."),
|
||||
maxlastfiles);
|
||||
break;
|
||||
|
||||
case RC_COMPLETION_POPUP_DELAY:
|
||||
str = _("The completion popup delay.");
|
||||
break;
|
||||
|
||||
case RC_COMPLETION_POPUP_MATH:
|
||||
str = _("Select to display the completion popup in math mode.");
|
||||
break;
|
||||
|
||||
case RC_COMPLETION_POPUP_TEXT:
|
||||
str = _("Select to display the completion popup in text mode.");
|
||||
break;
|
||||
|
||||
case RC_COMPLETION_POPUP_AFTER_COMPLETE:
|
||||
str = _("Show the completion popup without delay after non-unique completion attempt.");
|
||||
break;
|
||||
|
||||
case RC_COMPLETION_POPUP_DELAY:
|
||||
str = _("The inline completion delay.");
|
||||
break;
|
||||
|
||||
case RC_COMPLETION_INLINE_MATH:
|
||||
str = _("Select to display the inline completion in math mode.");
|
||||
break;
|
||||
|
||||
case RC_COMPLETION_INLINE_TEXT:
|
||||
str = _("Select to display the inline completion in text mode.");
|
||||
break;
|
||||
|
||||
case RC_COMPLETION_INLINE_DOTS:
|
||||
str = _("Use \"...\" to shorten long completions.");
|
||||
break;
|
||||
|
||||
case RC_NUMLASTFILES:
|
||||
str = bformat(_("Maximal number of lastfiles. Up to %1$d can appear in the file menu."),
|
||||
maxlastfiles);
|
||||
|
24
src/LyXRC.h
24
src/LyXRC.h
@ -49,6 +49,14 @@ public:
|
||||
RC_BINDFILE,
|
||||
RC_CHECKLASTFILES,
|
||||
RC_CHKTEX_COMMAND,
|
||||
RC_COMPLETION_INLINE_DELAY,
|
||||
RC_COMPLETION_INLINE_MATH,
|
||||
RC_COMPLETION_INLINE_TEXT,
|
||||
RC_COMPLETION_INLINE_DOTS,
|
||||
RC_COMPLETION_POPUP_DELAY,
|
||||
RC_COMPLETION_POPUP_MATH,
|
||||
RC_COMPLETION_POPUP_TEXT,
|
||||
RC_COMPLETION_POPUP_AFTER_COMPLETE,
|
||||
RC_CONVERTER,
|
||||
RC_CONVERTER_CACHE_MAXAGE,
|
||||
RC_COPIER,
|
||||
@ -409,6 +417,22 @@ public:
|
||||
bool full_screen_limit;
|
||||
/// Width of limited screen (in pixels) in fullscreen mode
|
||||
int full_screen_width;
|
||||
///
|
||||
double completion_inline_delay;
|
||||
///
|
||||
bool completion_inline_math;
|
||||
///
|
||||
bool completion_inline_text;
|
||||
///
|
||||
int completion_inline_dots;
|
||||
///
|
||||
double completion_popup_delay;
|
||||
///
|
||||
bool completion_popup_math;
|
||||
///
|
||||
bool completion_popup_text;
|
||||
///
|
||||
bool completion_popup_after_complete;
|
||||
};
|
||||
|
||||
|
||||
|
23
src/Text.cpp
23
src/Text.cpp
@ -564,6 +564,8 @@ void Text::insertChar(Cursor & cur, char_type c)
|
||||
|
||||
void Text::charInserted(Cursor & cur)
|
||||
{
|
||||
Paragraph & par = cur.paragraph();
|
||||
|
||||
// Here we call finishUndo for every 20 characters inserted.
|
||||
// This is from my experience how emacs does it. (Lgb)
|
||||
static unsigned int counter;
|
||||
@ -573,6 +575,27 @@ void Text::charInserted(Cursor & cur)
|
||||
cur.finishUndo();
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
// register word if a non-letter was entered
|
||||
if (cur.pos() > 1
|
||||
&& par.isLetter(cur.pos() - 2)
|
||||
&& !par.isLetter(cur.pos() - 1)) {
|
||||
// get the word in front of cursor
|
||||
BOOST_ASSERT(this == cur.text());
|
||||
CursorSlice focus = cur.top();
|
||||
focus.backwardPos();
|
||||
CursorSlice from = focus;
|
||||
CursorSlice to = focus;
|
||||
getWord(from, to, PREVIOUS_WORD);
|
||||
if (focus == from || to == from)
|
||||
return;
|
||||
docstring word
|
||||
= par.asString(cur.buffer(), from.pos(), to.pos(), false);
|
||||
|
||||
// register words longer than 5 characters
|
||||
if (word.length() > 5)
|
||||
cur.buffer().registerWord(word);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -730,6 +730,16 @@ pit_type TextMetrics::rowBreakPoint(int width, pit_type const pit,
|
||||
|
||||
pos_type const body_pos = par.beginOfBody();
|
||||
|
||||
// check for possible inline completion
|
||||
DocIterator const & inlineCompletionPos = bv_->inlineCompletionPos();
|
||||
pos_type inlineCompletionVPos = -1;
|
||||
if (inlineCompletionPos.inTexted()
|
||||
&& inlineCompletionPos.text() == text_
|
||||
&& inlineCompletionPos.pit() == pit) {
|
||||
// draw visually behind the previous character
|
||||
// FIXME: probably special RTL handling needed here
|
||||
inlineCompletionVPos = inlineCompletionPos.pos() - 1;
|
||||
}
|
||||
|
||||
// Now we iterate through until we reach the right margin
|
||||
// or the end of the par, then choose the possible break
|
||||
@ -748,6 +758,13 @@ pit_type TextMetrics::rowBreakPoint(int width, pit_type const pit,
|
||||
for ( ; i < end; ++i, ++fi) {
|
||||
int thiswidth = pm.singleWidth(i, *fi);
|
||||
|
||||
// add inline completion width
|
||||
if (inlineCompletionVPos == i) {
|
||||
docstring const & completion = bv_->inlineCompletion();
|
||||
if (completion.length() > 0)
|
||||
thiswidth += theFontMetrics(*fi).width(completion);
|
||||
}
|
||||
|
||||
// add the auto-hfill from label end to the body
|
||||
if (body_pos && i == body_pos) {
|
||||
FontMetrics const & fm = theFontMetrics(
|
||||
@ -830,6 +847,17 @@ int TextMetrics::rowWidth(int right_margin, pit_type const pit,
|
||||
int w = leftMargin(max_width_, pit, first);
|
||||
int label_end = labelEnd(pit);
|
||||
|
||||
// check for possible inline completion
|
||||
DocIterator const & inlineCompletionPos = bv_->inlineCompletionPos();
|
||||
pos_type inlineCompletionVPos = -1;
|
||||
if (inlineCompletionPos.inTexted()
|
||||
&& inlineCompletionPos.text() == text_
|
||||
&& inlineCompletionPos.pit() == pit) {
|
||||
// draw visually behind the previous character
|
||||
// FIXME: probably special RTL handling needed here
|
||||
inlineCompletionVPos = inlineCompletionPos.pos() - 1;
|
||||
}
|
||||
|
||||
pos_type const body_pos = par.beginOfBody();
|
||||
pos_type i = first;
|
||||
|
||||
@ -845,6 +873,13 @@ int TextMetrics::rowWidth(int right_margin, pit_type const pit,
|
||||
w = max(w, label_end);
|
||||
}
|
||||
w += pm.singleWidth(i, *fi);
|
||||
|
||||
// add inline completion width
|
||||
if (inlineCompletionVPos == i) {
|
||||
docstring const & completion = bv_->inlineCompletion();
|
||||
if (completion.length() > 0)
|
||||
w += theFontMetrics(*fi).width(completion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -862,7 +897,7 @@ int TextMetrics::rowWidth(int right_margin, pit_type const pit,
|
||||
|
||||
|
||||
Dimension TextMetrics::rowHeight(pit_type const pit, pos_type const first,
|
||||
pos_type const end) const
|
||||
pos_type const end, bool topBottomSpace) const
|
||||
{
|
||||
Paragraph const & par = text_->getPar(pit);
|
||||
// get the maximum ascent and the maximum descent
|
||||
@ -933,7 +968,7 @@ Dimension TextMetrics::rowHeight(pit_type const pit, pos_type const first,
|
||||
ParagraphList const & pars = text_->paragraphs();
|
||||
|
||||
// is it a top line?
|
||||
if (first == 0) {
|
||||
if (first == 0 && topBottomSpace) {
|
||||
BufferParams const & bufparams = buffer.params();
|
||||
// some parskips VERY EASY IMPLEMENTATION
|
||||
if (bufparams.paragraph_separation
|
||||
@ -1004,7 +1039,7 @@ Dimension TextMetrics::rowHeight(pit_type const pit, pos_type const first,
|
||||
}
|
||||
|
||||
// is it a bottom line?
|
||||
if (end >= par.size()) {
|
||||
if (end >= par.size() && topBottomSpace) {
|
||||
// add the layout spaces, for example before and after
|
||||
// a section, or between the items of a itemize or enumerate
|
||||
// environment
|
||||
@ -1039,7 +1074,7 @@ Dimension TextMetrics::rowHeight(pit_type const pit, pos_type const first,
|
||||
// following code in another method specially tailored for the
|
||||
// main Text. The following test is thus bogus.
|
||||
// Top and bottom margin of the document (only at top-level)
|
||||
if (main_text_) {
|
||||
if (main_text_ && topBottomSpace) {
|
||||
if (pit == 0 && first == 0)
|
||||
maxasc += 20;
|
||||
if (pit + 1 == pit_type(pars.size()) &&
|
||||
|
@ -124,6 +124,15 @@ public:
|
||||
|
||||
void drawParagraph(PainterInfo & pi, pit_type pit, int x, int y) const;
|
||||
|
||||
/// Returns the height of the row (width member is set to 0).
|
||||
/// If \c topBottomSpace is true, extra space is added for the
|
||||
/// top and bottom row.
|
||||
Dimension rowHeight(
|
||||
pit_type const pit,
|
||||
pos_type const first,
|
||||
pos_type const end,
|
||||
bool topBottomSpace = true) const;
|
||||
|
||||
private:
|
||||
///
|
||||
ParagraphMetrics & parMetrics(pit_type, bool redo_paragraph);
|
||||
@ -142,7 +151,7 @@ private:
|
||||
pit_type first
|
||||
) const;
|
||||
|
||||
/// sets row.width to the minimum space a row needs on the screen in pixel
|
||||
/// returns the minimum space a row needs on the screen in pixel
|
||||
int rowWidth(
|
||||
int right_margin,
|
||||
pit_type const pit,
|
||||
@ -150,13 +159,6 @@ private:
|
||||
pos_type const end
|
||||
) const;
|
||||
|
||||
/// Calculate and set the height of the row (width member is set to 0)
|
||||
Dimension rowHeight(
|
||||
pit_type const pit,
|
||||
pos_type const first,
|
||||
pos_type const end
|
||||
) const;
|
||||
|
||||
/// draw selection for a single row
|
||||
void drawRowSelection(PainterInfo & pi, int x, Row const & row,
|
||||
DocIterator const & beg, DocIterator const & end,
|
||||
|
@ -91,6 +91,12 @@ public:
|
||||
///
|
||||
virtual void restartCursor() = 0;
|
||||
|
||||
/// Update the completion popup and the inline completion state.
|
||||
/// If \c start is true, then a new completion might be started.
|
||||
/// If \c keep is true, an active completion will be kept active
|
||||
/// even though the cursor moved.
|
||||
virtual void updateCompletion(bool start, bool keep) = 0;
|
||||
|
||||
private:
|
||||
/// noncopyable
|
||||
LyXView(LyXView const &);
|
||||
|
@ -128,10 +128,11 @@ void GuiCommandBuffer::cancel()
|
||||
|
||||
void GuiCommandBuffer::dispatch()
|
||||
{
|
||||
dispatch(fromqstr(edit_->text()));
|
||||
QString cmd = edit_->text();
|
||||
view_->setFocus();
|
||||
edit_->setText(QString());
|
||||
edit_->clearFocus();
|
||||
dispatch(fromqstr(cmd));
|
||||
}
|
||||
|
||||
|
||||
|
585
src/frontends/qt4/GuiCompleter.cpp
Normal file
585
src/frontends/qt4/GuiCompleter.cpp
Normal file
@ -0,0 +1,585 @@
|
||||
/**
|
||||
* \file GuiCompleter.cpp
|
||||
* This file is part of LyX, the document processor.
|
||||
* Licence details can be found in the file COPYING.
|
||||
*
|
||||
* \author Stefan Schimanski
|
||||
*
|
||||
* Full author contact details are available in file CREDITS.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "GuiWorkArea.h"
|
||||
|
||||
#include "Buffer.h"
|
||||
#include "BufferView.h"
|
||||
#include "Cursor.h"
|
||||
#include "Dimension.h"
|
||||
#include "FuncRequest.h"
|
||||
#include "GuiView.h"
|
||||
#include "LyXFunc.h"
|
||||
#include "LyXRC.h"
|
||||
#include "version.h"
|
||||
|
||||
#include "support/debug.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QAbstractListModel>
|
||||
#include <QHeaderView>
|
||||
#include <QPainter>
|
||||
#include <QPixmapCache>
|
||||
#include <QScrollBar>
|
||||
#include <QItemDelegate>
|
||||
#include <QTreeView>
|
||||
#include <QTimer>
|
||||
|
||||
using namespace std;
|
||||
using namespace lyx::support;
|
||||
|
||||
namespace lyx {
|
||||
namespace frontend {
|
||||
|
||||
|
||||
class PixmapItemDelegate : public QItemDelegate {
|
||||
public:
|
||||
explicit PixmapItemDelegate(QObject *parent = 0)
|
||||
: QItemDelegate(parent) {}
|
||||
|
||||
protected:
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
QStyleOptionViewItemV3 opt = setOptions(index, option);
|
||||
QVariant value = index.data(Qt::DisplayRole);
|
||||
QPixmap pixmap = qvariant_cast<QPixmap>(value);
|
||||
const QSize size = pixmap.size();
|
||||
|
||||
// draw
|
||||
painter->save();
|
||||
drawBackground(painter, opt, index);
|
||||
painter->drawPixmap(option.rect.left() + (16 - size.width()) / 2,
|
||||
option.rect.top() + (option.rect.height() - size.height()) / 2,
|
||||
pixmap);
|
||||
drawFocus(painter, opt, option.rect);
|
||||
painter->restore();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class GuiCompletionModel : public QAbstractListModel {
|
||||
public:
|
||||
///
|
||||
GuiCompletionModel(QObject * parent, Inset::CompletionListPtr l)
|
||||
: QAbstractListModel(parent), list(l) {}
|
||||
///
|
||||
int columnCount(const QModelIndex & parent = QModelIndex()) const
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
///
|
||||
int rowCount(const QModelIndex & parent = QModelIndex()) const
|
||||
{
|
||||
if (list.get() == 0)
|
||||
return 0;
|
||||
else
|
||||
return list->size();
|
||||
}
|
||||
|
||||
///
|
||||
QVariant data(const QModelIndex & index, int role) const
|
||||
{
|
||||
if (list.get() == 0)
|
||||
return QVariant();
|
||||
|
||||
if (index.row() < 0 || index.row() >= rowCount())
|
||||
return QVariant();
|
||||
|
||||
if (role != Qt::DisplayRole && role != Qt::EditRole)
|
||||
return QVariant();
|
||||
|
||||
if (index.column() == 0)
|
||||
return toqstr(list->data(index.row()));
|
||||
else if (index.column() == 1) {
|
||||
// get icon from cache
|
||||
QPixmap scaled;
|
||||
QString const name = ":" + toqstr(list->icon(index.row()));
|
||||
if (!QPixmapCache::find("completion" + name, scaled)) {
|
||||
// load icon from disk
|
||||
QPixmap p = QPixmap(name);
|
||||
|
||||
// scale it to 16x16 or smaller
|
||||
scaled = p.scaled(min(16, p.width()), min(16, p.height()),
|
||||
Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
QPixmapCache::insert("completion" + name, scaled);
|
||||
}
|
||||
return scaled;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
private:
|
||||
Inset::CompletionListPtr list;
|
||||
};
|
||||
|
||||
|
||||
GuiCompleter::GuiCompleter(GuiWorkArea * gui, QObject * parent)
|
||||
: QCompleter(parent), gui_(gui)
|
||||
{
|
||||
// Setup the completion popup
|
||||
setModel(new GuiCompletionModel(this, Inset::CompletionListPtr()));
|
||||
setCompletionMode(QCompleter::PopupCompletion);
|
||||
setWidget(gui_);
|
||||
|
||||
// create the popup
|
||||
QTreeView *listView = new QTreeView;
|
||||
listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
listView->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
listView->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
listView->header()->hide();
|
||||
listView->setIndentation(0);
|
||||
setPopup(listView);
|
||||
popup()->setItemDelegateForColumn(1, new PixmapItemDelegate(popup()));
|
||||
|
||||
// create timeout timers
|
||||
popup_timer_.setSingleShot(true);
|
||||
inline_timer_.setSingleShot(true);
|
||||
connect(this, SIGNAL(highlighted(const QString &)),
|
||||
this, SLOT(popupHighlighted(const QString &)));
|
||||
connect(this, SIGNAL(activated(const QString &)),
|
||||
this, SLOT(popupActivated(const QString &)));
|
||||
connect(&popup_timer_, SIGNAL(timeout()),
|
||||
this, SLOT(showPopup()));
|
||||
connect(&inline_timer_, SIGNAL(timeout()),
|
||||
this, SLOT(showInline()));
|
||||
}
|
||||
|
||||
|
||||
GuiCompleter::~GuiCompleter()
|
||||
{
|
||||
popup()->hide();
|
||||
}
|
||||
|
||||
|
||||
bool GuiCompleter::eventFilter(QObject * watched, QEvent * e)
|
||||
{
|
||||
// hijack back the tab key from the popup
|
||||
// (which stole it from the workspace before)
|
||||
if (e->type() == QEvent::KeyPress && popupVisible()) {
|
||||
QKeyEvent *ke = static_cast<QKeyEvent *>(e);
|
||||
switch (ke->key()) {
|
||||
case Qt::Key_Tab:
|
||||
tab();
|
||||
ke->accept();
|
||||
return true;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
return QCompleter::eventFilter(watched, e);
|
||||
}
|
||||
|
||||
|
||||
bool GuiCompleter::popupPossible(Cursor const & cur) const
|
||||
{
|
||||
return QApplication::activeWindow()
|
||||
&& gui_->hasFocus()
|
||||
&& cur.inset().completionSupported(cur);
|
||||
}
|
||||
|
||||
|
||||
bool GuiCompleter::inlinePossible(Cursor const & cur) const
|
||||
{
|
||||
return cur.inset().inlineCompletionSupported(cur);
|
||||
}
|
||||
|
||||
|
||||
bool GuiCompleter::popupVisible() const
|
||||
{
|
||||
return popup()->isVisible();
|
||||
}
|
||||
|
||||
|
||||
bool GuiCompleter::inlineVisible() const
|
||||
{
|
||||
return !gui_->bufferView().inlineCompletionPos().empty();
|
||||
}
|
||||
|
||||
|
||||
void GuiCompleter::updateVisibility(Cursor & cur, bool start, bool keep, bool cursorInView)
|
||||
{
|
||||
// parameters which affect the completion
|
||||
bool moved = cur != old_cursor_;
|
||||
if (moved)
|
||||
old_cursor_ = cur;
|
||||
|
||||
bool possiblePopupState = popupPossible(cur) && cursorInView;
|
||||
bool possibleInlineState = inlinePossible(cur) && cursorInView;
|
||||
|
||||
// we moved or popup state is not ok for popup?
|
||||
if ((moved && !keep) || !possiblePopupState) {
|
||||
// stop an old completion timer
|
||||
if (popup_timer_.isActive())
|
||||
popup_timer_.stop();
|
||||
|
||||
// hide old popup
|
||||
if (popupVisible())
|
||||
popup()->hide();
|
||||
}
|
||||
|
||||
// we moved or inline state is not ok for inline completion?
|
||||
if ((moved && !keep) || !possibleInlineState) {
|
||||
// stop an old completion timer
|
||||
if (inline_timer_.isActive())
|
||||
inline_timer_.stop();
|
||||
|
||||
// hide old inline completion
|
||||
if (inlineVisible()) {
|
||||
gui_->bufferView().setInlineCompletion(DocIterator(), docstring());
|
||||
cur.updateFlags(Update::Force | Update::SinglePar);
|
||||
}
|
||||
}
|
||||
|
||||
// we inserted something and are in a possible popup state?
|
||||
if (!popupVisible() && possiblePopupState && start
|
||||
&& cur.inset().automaticPopupCompletion())
|
||||
popup_timer_.start(lyxrc.completion_popup_delay * 1000.0);
|
||||
|
||||
// we inserted something and are in a possible inline completion state?
|
||||
if (!inlineVisible() && possibleInlineState && start
|
||||
&& cur.inset().automaticInlineCompletion())
|
||||
inline_timer_.start(lyxrc.completion_inline_delay * 1000.0);
|
||||
|
||||
// update prefix if popup is visible or if it will be visible soon
|
||||
if (popupVisible() || inlineVisible()
|
||||
|| popup_timer_.isActive() || inline_timer_.isActive())
|
||||
updatePrefix(cur);
|
||||
}
|
||||
|
||||
|
||||
void GuiCompleter::updateVisibility(bool start, bool keep)
|
||||
{
|
||||
Cursor cur = gui_->bufferView().cursor();
|
||||
updateVisibility(cur, start, keep);
|
||||
if (cur.disp_.update())
|
||||
gui_->bufferView().processUpdateFlags(cur.disp_.update());
|
||||
}
|
||||
|
||||
|
||||
void GuiCompleter::updatePrefix(Cursor & cur)
|
||||
{
|
||||
// get new prefix. Do nothing if unchanged
|
||||
QString newPrefix = toqstr(cur.inset().completionPrefix(cur));
|
||||
if (newPrefix == completionPrefix())
|
||||
return;
|
||||
|
||||
// value which should be kept selected
|
||||
QString old = currentCompletion();
|
||||
if (old.length() == 0)
|
||||
old = last_selection_;
|
||||
|
||||
// update completer to new prefix
|
||||
setCompletionPrefix(newPrefix);
|
||||
|
||||
// update popup because its size might have changed
|
||||
if (popupVisible())
|
||||
updatePopup(cur);
|
||||
|
||||
// restore old selection
|
||||
setCurrentCompletion(old);
|
||||
|
||||
// if popup is not empty, the new selection will
|
||||
// be our last valid one
|
||||
QString const & s = currentCompletion();
|
||||
if (s.length() > 0)
|
||||
last_selection_ = s;
|
||||
else
|
||||
last_selection_ = old;
|
||||
|
||||
// update inline completion because the default
|
||||
// completion string might have changed
|
||||
if (inlineVisible())
|
||||
updateInline(cur, s);
|
||||
}
|
||||
|
||||
|
||||
void GuiCompleter::updateInline(Cursor & cur, QString const & completion)
|
||||
{
|
||||
if (!cur.inset().inlineCompletionSupported(cur))
|
||||
return;
|
||||
|
||||
// compute postfix
|
||||
docstring prefix = cur.inset().completionPrefix(cur);
|
||||
docstring postfix = from_utf8(fromqstr(completion.mid(prefix.length())));
|
||||
|
||||
// shorten it if necessary
|
||||
if (lyxrc.completion_inline_dots != -1
|
||||
&& postfix.size() > unsigned(lyxrc.completion_inline_dots))
|
||||
postfix = postfix.substr(0, lyxrc.completion_inline_dots - 1) + "...";
|
||||
|
||||
// set inline completion at cursor position
|
||||
size_t uniqueTo = max(longestUniqueCompletion().size(), prefix.size());
|
||||
gui_->bufferView().setInlineCompletion(cur, postfix, uniqueTo - prefix.size());
|
||||
cur.updateFlags(Update::Force | Update::SinglePar);
|
||||
}
|
||||
|
||||
|
||||
void GuiCompleter::updatePopup(Cursor & cur)
|
||||
{
|
||||
if (!cur.inset().completionSupported(cur))
|
||||
return;
|
||||
|
||||
if (completionCount() == 0)
|
||||
return;
|
||||
|
||||
// get dimensions of completion prefix
|
||||
Dimension dim;
|
||||
int x;
|
||||
int y;
|
||||
cur.inset().completionPosAndDim(cur, x, y, dim);
|
||||
QRect insetRect = QRect(x, y - dim.ascent() - 3, 200, dim.height() + 6);
|
||||
|
||||
// show/update popup
|
||||
complete(insetRect);
|
||||
QTreeView * p = static_cast<QTreeView *>(popup());
|
||||
p->setColumnWidth(0, popup()->width() - 22 - p->verticalScrollBar()->width());
|
||||
|
||||
// update highlight
|
||||
updateInline(cur, currentCompletion());
|
||||
}
|
||||
|
||||
|
||||
void GuiCompleter::updateModel(Cursor & cur, bool popupUpdate, bool inlineUpdate)
|
||||
{
|
||||
// value which should be kept selected
|
||||
QString old = currentCompletion();
|
||||
if (old.length() == 0)
|
||||
old = last_selection_;
|
||||
|
||||
// set new model
|
||||
setModel(new GuiCompletionModel(this, cur.inset().completionList(cur)));
|
||||
|
||||
// show popup
|
||||
if (popupUpdate)
|
||||
updatePopup(cur);
|
||||
|
||||
// restore old selection
|
||||
setCurrentCompletion(old);
|
||||
|
||||
// if popup is not empty, the new selection will
|
||||
// be our last valid one
|
||||
QString const & s = currentCompletion();
|
||||
if (s.length() > 0)
|
||||
last_selection_ = s;
|
||||
else
|
||||
last_selection_ = old;
|
||||
|
||||
// show inline completion
|
||||
if (inlineUpdate)
|
||||
updateInline(cur, currentCompletion());
|
||||
}
|
||||
|
||||
|
||||
void GuiCompleter::showPopup(Cursor & cur)
|
||||
{
|
||||
if (!popupPossible(cur))
|
||||
return;
|
||||
|
||||
updateModel(cur, true, inlineVisible());
|
||||
updatePrefix(cur);
|
||||
}
|
||||
|
||||
|
||||
void GuiCompleter::showInline(Cursor & cur)
|
||||
{
|
||||
if (!inlinePossible(cur))
|
||||
return;
|
||||
|
||||
updateModel(cur, popupVisible(), true);
|
||||
updatePrefix(cur);
|
||||
}
|
||||
|
||||
|
||||
void GuiCompleter::showPopup()
|
||||
{
|
||||
Cursor cur = gui_->bufferView().cursor();
|
||||
showPopup(cur);
|
||||
|
||||
// redraw if needed
|
||||
if (cur.disp_.update())
|
||||
gui_->bufferView().processUpdateFlags(cur.disp_.update());
|
||||
}
|
||||
|
||||
|
||||
void GuiCompleter::showInline()
|
||||
{
|
||||
Cursor cur = gui_->bufferView().cursor();
|
||||
showInline(cur);
|
||||
|
||||
// redraw if needed
|
||||
if (cur.disp_.update())
|
||||
gui_->bufferView().processUpdateFlags(cur.disp_.update());
|
||||
}
|
||||
|
||||
|
||||
void GuiCompleter::activate()
|
||||
{
|
||||
if (!popupVisible() && !inlineVisible())
|
||||
return;
|
||||
|
||||
// Complete with current selection in the popup.
|
||||
QString s = currentCompletion();
|
||||
popup()->hide();
|
||||
popupActivated(s);
|
||||
}
|
||||
|
||||
|
||||
void GuiCompleter::tab()
|
||||
{
|
||||
BufferView * bv = &gui_->bufferView();
|
||||
Cursor & cur = bv->cursor();
|
||||
|
||||
// check that inline completion is active
|
||||
if (!inlineVisible()) {
|
||||
// try to activate the inline completion
|
||||
if (cur.inset().inlineCompletionSupported(cur)) {
|
||||
showInline();
|
||||
return;
|
||||
}
|
||||
// or try popup
|
||||
if (!popupVisible() && cur.inset().completionSupported(cur)) {
|
||||
showPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If completion is active, at least complete by one character
|
||||
docstring prefix = cur.inset().completionPrefix(cur);
|
||||
docstring completion = from_utf8(fromqstr(currentCompletion()));
|
||||
if (completion.size() <= prefix.size()) {
|
||||
// finalize completion
|
||||
cur.inset().insertCompletion(cur, docstring(), true);
|
||||
popup()->hide();
|
||||
updateVisibility(false, false);
|
||||
return;
|
||||
}
|
||||
docstring nextchar = completion.substr(prefix.size(), 1);
|
||||
if (!cur.inset().insertCompletion(cur, nextchar, false))
|
||||
return;
|
||||
updatePrefix(cur);
|
||||
|
||||
// try to complete as far as it is unique
|
||||
docstring longestCompletion = longestUniqueCompletion();
|
||||
prefix = cur.inset().completionPrefix(cur);
|
||||
docstring postfix = longestCompletion.substr(min(longestCompletion.size(), prefix.size()));
|
||||
cur.inset().insertCompletion(cur, postfix, false);
|
||||
old_cursor_ = bv->cursor();
|
||||
updatePrefix(cur);
|
||||
|
||||
// show popup without delay because the completion was not unique
|
||||
if (lyxrc.completion_popup_after_complete
|
||||
&& !popupVisible()
|
||||
&& popup()->model()->rowCount() > 1)
|
||||
popup_timer_.start(0);
|
||||
|
||||
// redraw if needed
|
||||
if (cur.disp_.update())
|
||||
gui_->bufferView().processUpdateFlags(cur.disp_.update());
|
||||
}
|
||||
|
||||
|
||||
QString GuiCompleter::currentCompletion() const
|
||||
{
|
||||
if (!popup()->selectionModel()->hasSelection())
|
||||
return QString();
|
||||
|
||||
// Not sure if this is bug in Qt: currentIndex() always
|
||||
// return the first element in the list.
|
||||
QModelIndex idx = popup()->currentIndex();
|
||||
return popup()->model()->data(idx, Qt::EditRole).toString();
|
||||
}
|
||||
|
||||
|
||||
void GuiCompleter::setCurrentCompletion(QString const & s)
|
||||
{
|
||||
QAbstractItemModel const & model = *popup()->model();
|
||||
size_t n = model.rowCount();
|
||||
if (n == 0)
|
||||
return;
|
||||
|
||||
// select the first if s is empty
|
||||
if (s.length() == 0) {
|
||||
popup()->setCurrentIndex(model.index(0, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
// iterate through list until the s is found
|
||||
// FIXME: there must be a better way than this iteration
|
||||
size_t i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
QString const & is
|
||||
= model.data(model.index(i, 0), Qt::EditRole).toString();
|
||||
if (is == s)
|
||||
break;
|
||||
}
|
||||
|
||||
// select the first if none was found
|
||||
if (i == n)
|
||||
i = 0;
|
||||
|
||||
popup()->setCurrentIndex(model.index(i, 0));
|
||||
}
|
||||
|
||||
|
||||
docstring GuiCompleter::longestUniqueCompletion() const {
|
||||
QAbstractItemModel const & model = *popup()->model();
|
||||
QString s = currentCompletion();
|
||||
size_t n = model.rowCount();
|
||||
|
||||
// iterate through the completions and cut off where s differs
|
||||
for (size_t i = 0; i < n && s.length() > 0; ++i) {
|
||||
QString const & is
|
||||
= model.data(model.index(i, 0), Qt::EditRole).toString();
|
||||
|
||||
// find common prefix
|
||||
size_t j;
|
||||
size_t isn = is.length();
|
||||
size_t sn = s.length();
|
||||
for (j = 0; j < isn && j < sn; ++j) {
|
||||
if (s.at(j) != is.at(j))
|
||||
break;
|
||||
}
|
||||
s = s.left(j);
|
||||
}
|
||||
|
||||
return from_utf8(fromqstr(s));
|
||||
}
|
||||
|
||||
|
||||
void GuiCompleter::popupActivated(const QString & completion)
|
||||
{
|
||||
Cursor & cur = gui_->bufferView().cursor();
|
||||
docstring prefix = cur.inset().completionPrefix(cur);
|
||||
docstring postfix = from_utf8(fromqstr(completion.mid(prefix.length())));
|
||||
cur.inset().insertCompletion(cur, postfix, true);
|
||||
updateVisibility(cur, false);
|
||||
if (cur.disp_.update())
|
||||
gui_->bufferView().processUpdateFlags(cur.disp_.update());
|
||||
}
|
||||
|
||||
|
||||
void GuiCompleter::popupHighlighted(const QString & completion)
|
||||
{
|
||||
Cursor cur = gui_->bufferView().cursor();
|
||||
updateInline(cur, completion);
|
||||
if (cur.disp_.update())
|
||||
gui_->bufferView().processUpdateFlags(cur.disp_.update());
|
||||
}
|
||||
|
||||
} // namespace frontend
|
||||
} // namespace lyx
|
||||
|
||||
#include "GuiCompleter_moc.cpp"
|
115
src/frontends/qt4/GuiCompleter.h
Normal file
115
src/frontends/qt4/GuiCompleter.h
Normal file
@ -0,0 +1,115 @@
|
||||
// -*- C++ -*-
|
||||
/**
|
||||
* \file GuiCompleter.h
|
||||
* This file is part of LyX, the document processor.
|
||||
* Licence details can be found in the file COPYING.
|
||||
*
|
||||
* \author Stefan Schimanski
|
||||
*
|
||||
* Full author contact details are available in file CREDITS.
|
||||
*/
|
||||
|
||||
#ifndef GUICOMPLETER_H
|
||||
#define GUICOMPLETER_H
|
||||
|
||||
#include "frontends/WorkArea.h"
|
||||
|
||||
#include "DocIterator.h"
|
||||
#include "FuncRequest.h"
|
||||
#include "qt_helpers.h"
|
||||
#include "support/docstring.h"
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QCompleter>
|
||||
#include <QStringListModel>
|
||||
#include <QTimer>
|
||||
|
||||
namespace lyx {
|
||||
|
||||
class Buffer;
|
||||
|
||||
namespace frontend {
|
||||
|
||||
class GuiWorkArea;
|
||||
|
||||
class GuiCompleter : private QCompleter
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
///
|
||||
GuiCompleter(GuiWorkArea * gui, QObject * parent = 0);
|
||||
///
|
||||
virtual ~GuiCompleter();
|
||||
|
||||
///
|
||||
bool popupVisible() const;
|
||||
///
|
||||
bool inlineVisible() const;
|
||||
///
|
||||
bool popupPossible(Cursor const & cur) const;
|
||||
///
|
||||
bool inlinePossible(Cursor const & cur) const;
|
||||
/// Activate the current completion, i.e. finalize it.
|
||||
void activate();
|
||||
/// Do a completion as far as it is unique, but at least one character.
|
||||
void tab();
|
||||
|
||||
/// Update the visibility of the popup and the inline completion.
|
||||
/// This method might set the update flags of the cursor to request
|
||||
/// a redraw.
|
||||
void updateVisibility(Cursor & cur, bool start, bool keep, bool cursorInView = true);
|
||||
/// Update the visibility of the popup and the inline completion.
|
||||
/// This method handles the redraw if needed.
|
||||
void updateVisibility(bool start, bool keep);
|
||||
///
|
||||
QString currentCompletion() const;
|
||||
///
|
||||
docstring longestUniqueCompletion() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
/// Show the popup.
|
||||
void showPopup();
|
||||
/// Show the inline completion.
|
||||
void showInline();
|
||||
|
||||
private Q_SLOTS:
|
||||
///
|
||||
void popupActivated(const QString & completion);
|
||||
///
|
||||
void popupHighlighted(const QString & completion);
|
||||
|
||||
private:
|
||||
///
|
||||
void setCurrentCompletion(QString const & s);
|
||||
///
|
||||
void showPopup(Cursor & cur);
|
||||
///
|
||||
void showInline(Cursor & cur);
|
||||
///
|
||||
void updatePopup(Cursor & cur);
|
||||
///
|
||||
void updateInline(Cursor & cur, QString const & completion);
|
||||
///
|
||||
void updatePrefix(Cursor & cur);
|
||||
///
|
||||
void updateModel(Cursor & cur, bool popupUpdate, bool inlineUpdate);
|
||||
///
|
||||
bool eventFilter(QObject * watched, QEvent * event);
|
||||
|
||||
///
|
||||
GuiWorkArea * gui_;
|
||||
///
|
||||
DocIterator old_cursor_;
|
||||
///
|
||||
QTimer popup_timer_;
|
||||
///
|
||||
QTimer inline_timer_;
|
||||
///
|
||||
QString last_selection_;
|
||||
}; // GuiCompleter
|
||||
|
||||
} // namespace frontend
|
||||
} // namespace lyx
|
||||
|
||||
#endif // GUICOMPLETER_H
|
@ -260,6 +260,20 @@ PrefInput::PrefInput(GuiPreferences * form, QWidget * parent)
|
||||
this, SIGNAL(changed()));
|
||||
connect(secondKeymapED, SIGNAL(textChanged(QString)),
|
||||
this, SIGNAL(changed()));
|
||||
connect(inlineDelaySB, SIGNAL(valueChanged(double)),
|
||||
this, SIGNAL(changed()));
|
||||
connect(inlineMathCB, SIGNAL(clicked()),
|
||||
this, SIGNAL(changed()));
|
||||
connect(inlineTextCB, SIGNAL(clicked()),
|
||||
this, SIGNAL(changed()));
|
||||
connect(inlineDotsCB, SIGNAL(clicked()),
|
||||
this, SIGNAL(changed()));
|
||||
connect(popupDelaySB, SIGNAL(valueChanged(double)),
|
||||
this, SIGNAL(changed()));
|
||||
connect(popupMathCB, SIGNAL(clicked()),
|
||||
this, SIGNAL(changed()));
|
||||
connect(popupTextCB, SIGNAL(clicked()),
|
||||
this, SIGNAL(changed()));
|
||||
connect(mouseWheelSpeedSB, SIGNAL(valueChanged(double)),
|
||||
this, SIGNAL(changed()));
|
||||
}
|
||||
@ -271,6 +285,15 @@ void PrefInput::apply(LyXRC & rc) const
|
||||
rc.use_kbmap = keymapCB->isChecked();
|
||||
rc.primary_kbmap = internal_path(fromqstr(firstKeymapED->text()));
|
||||
rc.secondary_kbmap = internal_path(fromqstr(secondKeymapED->text()));
|
||||
rc.completion_inline_delay = inlineDelaySB->value();
|
||||
rc.completion_inline_math = inlineMathCB->isChecked();
|
||||
rc.completion_inline_text = inlineTextCB->isChecked();
|
||||
rc.completion_inline_dots = inlineDotsCB->isChecked() ? 13 : -1;
|
||||
rc.completion_popup_delay = popupDelaySB->value();
|
||||
rc.completion_popup_math = popupMathCB->isChecked();
|
||||
rc.completion_popup_text = popupTextCB->isChecked();
|
||||
rc.completion_popup_after_complete
|
||||
= popupAfterCompleteCB->isChecked();
|
||||
rc.mouse_wheel_speed = mouseWheelSpeedSB->value();
|
||||
}
|
||||
|
||||
@ -281,6 +304,14 @@ void PrefInput::update(LyXRC const & rc)
|
||||
keymapCB->setChecked(rc.use_kbmap);
|
||||
firstKeymapED->setText(toqstr(external_path(rc.primary_kbmap)));
|
||||
secondKeymapED->setText(toqstr(external_path(rc.secondary_kbmap)));
|
||||
inlineDelaySB->setValue(rc.completion_inline_delay);
|
||||
inlineMathCB->setChecked(rc.completion_inline_math);
|
||||
inlineTextCB->setChecked(rc.completion_inline_text);
|
||||
inlineDotsCB->setChecked(rc.completion_inline_dots != -1);
|
||||
popupDelaySB->setValue(rc.completion_popup_delay);
|
||||
popupMathCB->setChecked(rc.completion_popup_math);
|
||||
popupTextCB->setChecked(rc.completion_popup_text);
|
||||
popupAfterCompleteCB->setChecked(rc.completion_popup_after_complete);
|
||||
mouseWheelSpeedSB->setValue(rc.mouse_wheel_speed);
|
||||
}
|
||||
|
||||
|
@ -1044,6 +1044,24 @@ FuncStatus GuiView::getStatus(FuncRequest const & cmd)
|
||||
break;
|
||||
}
|
||||
|
||||
case LFUN_COMPLETION_INLINE:
|
||||
if (!d.current_work_area_
|
||||
|| !d.current_work_area_->completer().inlinePossible(view()->cursor()))
|
||||
enable = false;
|
||||
break;
|
||||
|
||||
case LFUN_COMPLETION_POPUP:
|
||||
if (!d.current_work_area_
|
||||
|| !d.current_work_area_->completer().popupPossible(view()->cursor()))
|
||||
enable = false;
|
||||
break;
|
||||
|
||||
case LFUN_COMPLETION_COMPLETE:
|
||||
if (!d.current_work_area_
|
||||
|| !d.current_work_area_->completer().inlinePossible(view()->cursor()))
|
||||
enable = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!view()) {
|
||||
enable = false;
|
||||
@ -1815,6 +1833,11 @@ bool GuiView::dispatch(FuncRequest const & cmd)
|
||||
setFocus();
|
||||
break;
|
||||
|
||||
case LFUN_COMPLETION_INLINE:
|
||||
if (d.current_work_area_)
|
||||
d.current_work_area_->completer().showInline();
|
||||
break;
|
||||
|
||||
case LFUN_SPLIT_VIEW:
|
||||
if (Buffer * buf = buffer()) {
|
||||
string const orientation = cmd.getArg(0);
|
||||
@ -1824,7 +1847,6 @@ bool GuiView::dispatch(FuncRequest const & cmd)
|
||||
GuiWorkArea * wa = twa->addWorkArea(*buf, *this);
|
||||
setCurrentWorkArea(wa);
|
||||
}
|
||||
break;
|
||||
|
||||
case LFUN_CLOSE_TAB_GROUP:
|
||||
if (TabWorkArea * twa = d.currentTabWorkArea()) {
|
||||
@ -1836,6 +1858,16 @@ bool GuiView::dispatch(FuncRequest const & cmd)
|
||||
// No more work area, switch to the background widget.
|
||||
d.setBackground();
|
||||
}
|
||||
|
||||
case LFUN_COMPLETION_POPUP:
|
||||
if (d.current_work_area_)
|
||||
d.current_work_area_->completer().showPopup();
|
||||
break;
|
||||
|
||||
|
||||
case LFUN_COMPLETION_COMPLETE:
|
||||
if (d.current_work_area_)
|
||||
d.current_work_area_->completer().tab();
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -1939,6 +1971,13 @@ void GuiView::restartCursor()
|
||||
updateToolbars();
|
||||
}
|
||||
|
||||
|
||||
void GuiView::updateCompletion(bool start, bool keep)
|
||||
{
|
||||
if (d.current_work_area_)
|
||||
d.current_work_area_->completer().updateVisibility(start, keep);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// This list should be kept in sync with the list of insets in
|
||||
|
@ -246,6 +246,9 @@ public:
|
||||
///
|
||||
void disconnectDialog(std::string const & name);
|
||||
|
||||
///
|
||||
void updateCompletion(bool start, bool keep);
|
||||
|
||||
private:
|
||||
///
|
||||
void lfunUiToggle(FuncRequest const & cmd);
|
||||
|
@ -189,9 +189,9 @@ SyntheticMouseEvent::SyntheticMouseEvent()
|
||||
|
||||
GuiWorkArea::GuiWorkArea(Buffer & buffer, GuiView & lv)
|
||||
: buffer_view_(new BufferView(buffer)), lyx_view_(&lv),
|
||||
cursor_visible_(false),
|
||||
need_resize_(false), schedule_redraw_(false),
|
||||
preedit_lines_(1)
|
||||
cursor_visible_(false),
|
||||
need_resize_(false), schedule_redraw_(false),
|
||||
preedit_lines_(1), completer_(this)
|
||||
{
|
||||
buffer.workAreaManager().add(this);
|
||||
// Setup the signals
|
||||
@ -242,7 +242,6 @@ GuiWorkArea::GuiWorkArea(Buffer & buffer, GuiView & lv)
|
||||
}
|
||||
|
||||
|
||||
|
||||
GuiWorkArea::~GuiWorkArea()
|
||||
{
|
||||
buffer_view_->buffer().workAreaManager().remove(this);
|
||||
@ -386,6 +385,7 @@ void GuiWorkArea::dispatch(FuncRequest const & cmd0, KeyModifier mod)
|
||||
|
||||
// Skip these when selecting
|
||||
if (cmd.action != LFUN_MOUSE_MOTION) {
|
||||
completer_.updateVisibility(false, false);
|
||||
lyx_view_->updateLayoutList();
|
||||
lyx_view_->updateToolbars();
|
||||
}
|
||||
@ -452,15 +452,20 @@ void GuiWorkArea::showCursor()
|
||||
int h = asc + des;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
buffer_view_->cursor().getPos(x, y);
|
||||
Cursor & cur = buffer_view_->cursor();
|
||||
cur.getPos(x, y);
|
||||
y -= asc;
|
||||
|
||||
// if it doesn't touch the screen, don't try to show it
|
||||
bool cursorInView = true;
|
||||
if (y + h < 0 || y >= viewport()->height())
|
||||
return;
|
||||
cursorInView = false;
|
||||
|
||||
cursor_visible_ = true;
|
||||
showCursor(x, y, h, shape);
|
||||
// show cursor on screen
|
||||
if (cursorInView) {
|
||||
cursor_visible_ = true;
|
||||
showCursor(x, y, h, shape);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -727,6 +732,24 @@ void GuiWorkArea::generateSyntheticMouseEvent()
|
||||
|
||||
void GuiWorkArea::keyPressEvent(QKeyEvent * ev)
|
||||
{
|
||||
// intercept some keys if completion popup is visible
|
||||
if (completer_.popupVisible()) {
|
||||
switch (ev->key()) {
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
completer_.activate();
|
||||
ev->accept();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// intercept tab for inline completion
|
||||
if (ev->key() == Qt::Key_Tab) {
|
||||
completer_.tab();
|
||||
ev->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
// do nothing if there are other events
|
||||
// (the auto repeated events come too fast)
|
||||
// \todo FIXME: remove hard coded Qt keys, process the key binding
|
||||
|
@ -14,8 +14,12 @@
|
||||
#define WORKAREA_H
|
||||
|
||||
#include "frontends/WorkArea.h"
|
||||
#include "frontends/qt4/GuiCompleter.h"
|
||||
|
||||
#include "DocIterator.h"
|
||||
#include "FuncRequest.h"
|
||||
#include "qt_helpers.h"
|
||||
#include "support/docstring.h"
|
||||
#include "support/Timeout.h"
|
||||
|
||||
#include <QAbstractScrollArea>
|
||||
@ -44,6 +48,7 @@ class Buffer;
|
||||
namespace frontend {
|
||||
|
||||
class GuiView;
|
||||
class GuiWorkArea;
|
||||
|
||||
/// types of cursor in work area
|
||||
enum CursorShape {
|
||||
@ -129,6 +134,9 @@ public:
|
||||
///
|
||||
void resizeBufferView();
|
||||
|
||||
///
|
||||
GuiCompleter & completer() { return completer_; }
|
||||
|
||||
Q_SIGNALS:
|
||||
///
|
||||
void titleChanged(GuiWorkArea *);
|
||||
@ -149,6 +157,8 @@ private Q_SLOTS:
|
||||
void close();
|
||||
|
||||
private:
|
||||
friend class GuiCompleter;
|
||||
|
||||
/// update the passed area.
|
||||
void update(int x, int y, int w, int h);
|
||||
///
|
||||
@ -229,6 +239,9 @@ private:
|
||||
bool schedule_redraw_;
|
||||
///
|
||||
int preedit_lines_;
|
||||
|
||||
///
|
||||
GuiCompleter completer_;
|
||||
}; // GuiWorkArea
|
||||
|
||||
|
||||
|
@ -73,6 +73,7 @@ SOURCEFILES = \
|
||||
GuiClipboard.cpp \
|
||||
GuiCommandBuffer.cpp \
|
||||
GuiCommandEdit.cpp \
|
||||
GuiCompleter.cpp \
|
||||
GuiDelimiter.cpp \
|
||||
GuiDialog.cpp \
|
||||
GuiDocument.cpp \
|
||||
@ -172,6 +173,7 @@ MOCHEADER = \
|
||||
GuiClipboard.h \
|
||||
GuiCommandBuffer.h \
|
||||
GuiCommandEdit.h \
|
||||
GuiCompleter.h \
|
||||
GuiDelimiter.h \
|
||||
GuiDialog.h \
|
||||
GuiDocument.h \
|
||||
|
@ -5,8 +5,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>392</width>
|
||||
<height>297</height>
|
||||
<width>432</width>
|
||||
<height>567</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
@ -104,6 +104,190 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox" >
|
||||
<property name="title" >
|
||||
<string>Completion</string>
|
||||
</property>
|
||||
<property name="flat" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2" >
|
||||
<property name="title" >
|
||||
<string>Popup</string>
|
||||
</property>
|
||||
<property name="flat" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<widget class="QCheckBox" name="popupMathCB" >
|
||||
<property name="toolTip" >
|
||||
<string>Show the popup in math mode after the delay.</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Automatically show in math mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="popupTextCB" >
|
||||
<property name="toolTip" >
|
||||
<string>Show the popup after the set delay in text mode.</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Automatically show in text mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="popupAfterCompleteCB" >
|
||||
<property name="toolTip" >
|
||||
<string>When the TAB completion is not unique, there won't be a delay of the popup. It will be shown right away.</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Show without delay for non-unique completions</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3" >
|
||||
<property name="text" >
|
||||
<string>Delay</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="popupDelaySB" >
|
||||
<property name="toolTip" >
|
||||
<string>After the cursor has not moved for this time, the completion popup is shown if it is available.</string>
|
||||
</property>
|
||||
<property name="maximum" >
|
||||
<double>10.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep" >
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label" >
|
||||
<property name="text" >
|
||||
<string>s</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3" >
|
||||
<property name="title" >
|
||||
<string>Inline</string>
|
||||
</property>
|
||||
<property name="flat" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<widget class="QCheckBox" name="inlineMathCB" >
|
||||
<property name="toolTip" >
|
||||
<string>Show the grey inline completion behind the cursor in math mode after the delay.</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Automatically show in math mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="inlineTextCB" >
|
||||
<property name="toolTip" >
|
||||
<string>Show the grey inline completion behind the cursor in text mode after the delay.</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Automatically show in text mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="inlineDotsCB" >
|
||||
<property name="toolTip" >
|
||||
<string>Long completions are cut-off and shown with "...".</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Use "..." to shorten long completions</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4" >
|
||||
<property name="text" >
|
||||
<string>Delay</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="inlineDelaySB" >
|
||||
<property name="toolTip" >
|
||||
<string>After the cursor has not moved for this time, the inline completion is shown if it is available.</string>
|
||||
</property>
|
||||
<property name="maximum" >
|
||||
<double>10.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep" >
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2" >
|
||||
<property name="text" >
|
||||
<string>s</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="mouseGB" >
|
||||
<property name="title" >
|
||||
@ -144,7 +328,7 @@
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
<property name="value" >
|
||||
<double>1.000000000000000</double>
|
||||
<double>1.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -175,7 +359,7 @@
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
<height>51</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
|
@ -20,6 +20,8 @@
|
||||
|
||||
#include "support/strfwd.h"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace lyx {
|
||||
@ -284,6 +286,47 @@ public:
|
||||
/// return true if the inset should be removed automatically
|
||||
virtual bool autoDelete() const;
|
||||
|
||||
class CompletionList {
|
||||
public:
|
||||
///
|
||||
virtual ~CompletionList() {}
|
||||
///
|
||||
virtual size_t size() const =0;
|
||||
/// returns the string shown in the gui.
|
||||
virtual docstring data(size_t idx) const =0;
|
||||
/// returns the resource string used to load an icon.
|
||||
virtual std::string icon(size_t idx) const { return std::string(); }
|
||||
};
|
||||
typedef boost::shared_ptr<CompletionList> CompletionListPtr;
|
||||
|
||||
/// Returns true if the inset supports completions.
|
||||
virtual bool completionSupported(Cursor const &) const { return false; }
|
||||
/// Returns true if the inset supports inline completions at the
|
||||
/// cursor position. In this case the completion might be stored
|
||||
/// in the BufferView's inlineCompletion property.
|
||||
virtual bool inlineCompletionSupported(Cursor const & cur) const { return false; }
|
||||
/// Return true if the inline completion should be automatic.
|
||||
virtual bool automaticInlineCompletion() const { return true; }
|
||||
/// Return true if the popup completion should be automatic.
|
||||
virtual bool automaticPopupCompletion() const { return true; }
|
||||
/// Returns completion suggestions at cursor position. Return an
|
||||
/// null pointer if no completion is a available or possible.
|
||||
virtual CompletionListPtr completionList(Cursor const &) const
|
||||
{
|
||||
return CompletionListPtr();
|
||||
}
|
||||
/// Returns the completion prefix to filter the suggestions for completion.
|
||||
/// This is only called if completionList returned a non-null list.
|
||||
virtual docstring completionPrefix(Cursor const &) const { return docstring(); }
|
||||
/// Do a completion at the cursor position. Return true on success.
|
||||
/// The completion does not contain the prefix. If finished is true, the
|
||||
/// completion is final. If finished is false, completion might only be
|
||||
/// a partial completion.
|
||||
virtual bool insertCompletion(Cursor & cur, docstring const & completion,
|
||||
bool finished) { return false; }
|
||||
/// Get the completion inset position and size
|
||||
virtual void completionPosAndDim(Cursor const &, int & x, int & y, Dimension & dim) const {}
|
||||
|
||||
/// returns true if the inset can hold an inset of given type
|
||||
virtual bool insetAllowed(InsetCode) const { return false; }
|
||||
/// should this inset use the empty layout by default rather than
|
||||
|
@ -65,6 +65,36 @@ namespace lyx {
|
||||
using graphics::PreviewLoader;
|
||||
|
||||
|
||||
class TextCompletionList : public Inset::CompletionList {
|
||||
public:
|
||||
///
|
||||
TextCompletionList(Cursor const & cur)
|
||||
: buf_(cur.buffer()), it_(buf_.registeredWords().begin()), pos_(0) {}
|
||||
///
|
||||
virtual ~TextCompletionList() {}
|
||||
|
||||
///
|
||||
virtual size_t size() const {
|
||||
return buf_.registeredWords().size();
|
||||
}
|
||||
///
|
||||
virtual docstring data(size_t idx) const {
|
||||
std::set<docstring>::iterator it
|
||||
= buf_.registeredWords().begin();
|
||||
for (size_t i = 0; i < idx; ++i)
|
||||
it++;
|
||||
return *it;
|
||||
}
|
||||
|
||||
private:
|
||||
Buffer const & buf_;
|
||||
std::set<docstring>::iterator const it_;
|
||||
size_t pos_;
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
InsetText::InsetText(BufferParams const & bp)
|
||||
: drawFrame_(false), frame_color_(Color_insetframe)
|
||||
{
|
||||
@ -444,4 +474,98 @@ void InsetText::updateLabels(Buffer const & buf, ParIterator const & it)
|
||||
}
|
||||
|
||||
|
||||
bool InsetText::completionSupported(Cursor const & cur) const
|
||||
{
|
||||
Cursor const & bvCur = cur.bv().cursor();
|
||||
if (&bvCur.inset() != this)
|
||||
return false;
|
||||
Paragraph const & par = cur.paragraph();
|
||||
return cur.pos() > 0
|
||||
&& !par.isLetter(cur.pos())
|
||||
&& par.isLetter(cur.pos() - 1);
|
||||
}
|
||||
|
||||
|
||||
bool InsetText::inlineCompletionSupported(Cursor const & cur) const
|
||||
{
|
||||
return completionSupported(cur);
|
||||
}
|
||||
|
||||
|
||||
bool InsetText::automaticInlineCompletion() const
|
||||
{
|
||||
return lyxrc.completion_inline_text;
|
||||
}
|
||||
|
||||
|
||||
bool InsetText::automaticPopupCompletion() const
|
||||
{
|
||||
return lyxrc.completion_popup_text;
|
||||
}
|
||||
|
||||
|
||||
Inset::CompletionListPtr InsetText::completionList(Cursor const & cur) const
|
||||
{
|
||||
if (!completionSupported(cur))
|
||||
return CompletionListPtr();
|
||||
|
||||
return CompletionListPtr(new TextCompletionList(cur));
|
||||
}
|
||||
|
||||
|
||||
docstring InsetText::previousWord(Buffer const & buffer, CursorSlice const & sl) const
|
||||
{
|
||||
CursorSlice from = sl;
|
||||
CursorSlice to = sl;
|
||||
text_.getWord(from, to, PREVIOUS_WORD);
|
||||
if (sl == from || to == from)
|
||||
return docstring();
|
||||
|
||||
Paragraph const & par = sl.paragraph();
|
||||
return par.asString(buffer, from.pos(), to.pos(), false);
|
||||
}
|
||||
|
||||
|
||||
docstring InsetText::completionPrefix(Cursor const & cur) const
|
||||
{
|
||||
if (!completionSupported(cur))
|
||||
return docstring();
|
||||
|
||||
return previousWord(cur.buffer(), cur.top());
|
||||
}
|
||||
|
||||
|
||||
bool InsetText::insertCompletion(Cursor & cur, docstring const & s,
|
||||
bool finished)
|
||||
{
|
||||
if (!completionSupported(cur))
|
||||
return false;
|
||||
|
||||
cur.insert(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void InsetText::completionPosAndDim(Cursor const & cur, int & x, int & y,
|
||||
Dimension & dim) const
|
||||
{
|
||||
// get word in front of cursor
|
||||
docstring word = previousWord(cur.buffer(), cur.top());
|
||||
DocIterator wordStart = cur;
|
||||
wordStart.pos() -= word.length();
|
||||
|
||||
// get position on screen of the word start
|
||||
Point lxy = cur.bv().getPos(wordStart, false);
|
||||
x = lxy.x_;
|
||||
y = lxy.y_;
|
||||
|
||||
// Calculate dimensions of the word
|
||||
TextMetrics const & tm = cur.bv().textMetrics(&text_);
|
||||
dim = tm.rowHeight(cur.pit(), wordStart.pos(), cur.pos(), false);
|
||||
Point rxy = cur.bv().getPos(cur, cur.boundary());
|
||||
dim.wid = abs(rxy.x_ - x);
|
||||
x = (rxy.x_ < x) ? x - dim.wid : x; // for RTL
|
||||
}
|
||||
|
||||
|
||||
} // namespace lyx
|
||||
|
@ -141,6 +141,23 @@ public:
|
||||
///
|
||||
virtual Inset * clone() const;
|
||||
|
||||
///
|
||||
bool completionSupported(Cursor const &) const;
|
||||
///
|
||||
bool inlineCompletionSupported(Cursor const & cur) const;
|
||||
///
|
||||
bool automaticInlineCompletion() const;
|
||||
///
|
||||
bool automaticPopupCompletion() const;
|
||||
///
|
||||
CompletionListPtr completionList(Cursor const & cur) const;
|
||||
///
|
||||
docstring completionPrefix(Cursor const & cur) const;
|
||||
///
|
||||
bool insertCompletion(Cursor & cur, docstring const & s, bool finished);
|
||||
///
|
||||
void completionPosAndDim(Cursor const &, int & x, int & y, Dimension & dim) const;
|
||||
|
||||
protected:
|
||||
///
|
||||
virtual void doDispatch(Cursor & cur, FuncRequest & cmd);
|
||||
@ -154,6 +171,8 @@ private:
|
||||
ColorCode frame_color_;
|
||||
///
|
||||
mutable pit_type old_pit;
|
||||
///
|
||||
docstring previousWord(Buffer const & buffer, CursorSlice const & sl) const;
|
||||
|
||||
public:
|
||||
///
|
||||
|
@ -415,6 +415,10 @@ enum kb_action {
|
||||
LFUN_UI_TOGGLE,
|
||||
LFUN_SPLIT_VIEW,
|
||||
LFUN_CLOSE_TAB_GROUP,
|
||||
// 320
|
||||
LFUN_COMPLETION_POPUP,
|
||||
LFUN_COMPLETION_INLINE,
|
||||
LFUN_COMPLETION_COMPLETE,
|
||||
LFUN_LASTACTION // end of the table
|
||||
};
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "MathSupport.h"
|
||||
|
||||
#include "Bidi.h"
|
||||
#include "Buffer.h"
|
||||
#include "BufferView.h"
|
||||
#include "CoordCache.h"
|
||||
#include "Cursor.h"
|
||||
@ -56,6 +57,8 @@
|
||||
#include "support/textutils.h"
|
||||
#include "support/docstream.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
@ -1585,6 +1588,90 @@ bool InsetMathNest::script(Cursor & cur, bool up,
|
||||
}
|
||||
|
||||
|
||||
bool InsetMathNest::completionSupported(Cursor const & cur) const
|
||||
{
|
||||
return cur.inMacroMode();
|
||||
}
|
||||
|
||||
|
||||
bool InsetMathNest::inlineCompletionSupported(Cursor const & cur) const
|
||||
{
|
||||
return cur.inMacroMode();
|
||||
}
|
||||
|
||||
|
||||
bool InsetMathNest::automaticInlineCompletion() const
|
||||
{
|
||||
return lyxrc.completion_inline_math;
|
||||
}
|
||||
|
||||
|
||||
bool InsetMathNest::automaticPopupCompletion() const
|
||||
{
|
||||
return lyxrc.completion_popup_math;
|
||||
}
|
||||
|
||||
|
||||
Inset::CompletionListPtr InsetMathNest::completionList(Cursor const & cur) const
|
||||
{
|
||||
if (!cur.inMacroMode())
|
||||
return CompletionListPtr();
|
||||
|
||||
return CompletionListPtr(new MathCompletionList(cur));
|
||||
}
|
||||
|
||||
|
||||
docstring InsetMathNest::completionPrefix(Cursor const & cur) const
|
||||
{
|
||||
if (!cur.inMacroMode())
|
||||
return docstring();
|
||||
|
||||
return cur.activeMacro()->name();
|
||||
}
|
||||
|
||||
|
||||
bool InsetMathNest::insertCompletion(Cursor & cur, docstring const & s,
|
||||
bool finished)
|
||||
{
|
||||
if (!cur.inMacroMode())
|
||||
return false;
|
||||
|
||||
// append completion to active macro
|
||||
InsetMathUnknown * inset = cur.activeMacro();
|
||||
inset->setName(inset->name() + s);
|
||||
|
||||
// finish macro
|
||||
if (finished) {
|
||||
#if 0
|
||||
// FIXME: this creates duplicates in the completion popup
|
||||
// which looks ugly. Moreover the changes the list lengths
|
||||
// which seems to
|
||||
confuse the popup as well.
|
||||
MathCompletionList::addToFavorites(inset->name());
|
||||
#endif
|
||||
lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, " "));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void InsetMathNest::completionPosAndDim(Cursor const & cur, int & x, int & y,
|
||||
Dimension & dim) const
|
||||
{
|
||||
Inset const * inset = cur.activeMacro();
|
||||
if (!inset)
|
||||
return;
|
||||
|
||||
// get inset dimensions
|
||||
dim = cur.bv().coordCache().insets().dim(inset);
|
||||
Point xy
|
||||
= cur.bv().coordCache().insets().xy(inset);
|
||||
x = xy.x_;
|
||||
y = xy.y_;
|
||||
}
|
||||
|
||||
|
||||
bool InsetMathNest::cursorMathForward(Cursor & cur)
|
||||
{
|
||||
if (cur.pos() != cur.lastpos() && cur.openable(cur.nextAtom())) {
|
||||
@ -1622,4 +1709,150 @@ bool InsetMathNest::cursorMathBackward(Cursor & cur)
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
MathCompletionList::MathCompletionList(Cursor const & cur)
|
||||
{
|
||||
// fill it with macros from the buffer
|
||||
Buffer::MacroNameSet macros;
|
||||
cur.buffer().listMacroNames(macros);
|
||||
Buffer::MacroNameSet::const_iterator it;
|
||||
for (it = macros.begin(); it != macros.end(); ++it) {
|
||||
if (cur.buffer().getMacro(*it, cur, false))
|
||||
locals.push_back("\\" + *it);
|
||||
}
|
||||
sort(locals.begin(), locals.end());
|
||||
|
||||
if (globals.size() > 0)
|
||||
return;
|
||||
|
||||
// fill in global macros
|
||||
macros.clear();
|
||||
MacroTable::globalMacros().getMacroNames(macros);
|
||||
lyxerr << "Globals completion macros: ";
|
||||
for (it = macros.begin(); it != macros.end(); ++it) {
|
||||
lyxerr << "\\" + *it << " ";
|
||||
globals.push_back("\\" + *it);
|
||||
}
|
||||
lyxerr << std::endl;
|
||||
|
||||
// fill in global commands
|
||||
globals.push_back(from_ascii("\\boxed"));
|
||||
globals.push_back(from_ascii("\\fbox"));
|
||||
globals.push_back(from_ascii("\\framebox"));
|
||||
globals.push_back(from_ascii("\\makebox"));
|
||||
globals.push_back(from_ascii("\\kern"));
|
||||
globals.push_back(from_ascii("\\xrightarrow"));
|
||||
globals.push_back(from_ascii("\\xleftarrow"));
|
||||
globals.push_back(from_ascii("\\split"));
|
||||
globals.push_back(from_ascii("\\gathered"));
|
||||
globals.push_back(from_ascii("\\aligned"));
|
||||
globals.push_back(from_ascii("\\alignedat"));
|
||||
globals.push_back(from_ascii("\\cases"));
|
||||
globals.push_back(from_ascii("\\substack"));
|
||||
globals.push_back(from_ascii("\\subarray"));
|
||||
globals.push_back(from_ascii("\\array"));
|
||||
globals.push_back(from_ascii("\\sqrt"));
|
||||
globals.push_back(from_ascii("\\root"));
|
||||
globals.push_back(from_ascii("\\tabular"));
|
||||
globals.push_back(from_ascii("\\stackrel"));
|
||||
globals.push_back(from_ascii("\\binom"));
|
||||
globals.push_back(from_ascii("\\choose"));
|
||||
globals.push_back(from_ascii("\\choose"));
|
||||
globals.push_back(from_ascii("\\frac"));
|
||||
globals.push_back(from_ascii("\\over"));
|
||||
globals.push_back(from_ascii("\\nicefrac"));
|
||||
globals.push_back(from_ascii("\\unitfrac"));
|
||||
globals.push_back(from_ascii("\\unitfracthree"));
|
||||
globals.push_back(from_ascii("\\unitone"));
|
||||
globals.push_back(from_ascii("\\unittwo"));
|
||||
globals.push_back(from_ascii("\\infer"));
|
||||
globals.push_back(from_ascii("\\atop"));
|
||||
globals.push_back(from_ascii("\\lefteqn"));
|
||||
globals.push_back(from_ascii("\\boldsymbol"));
|
||||
globals.push_back(from_ascii("\\color"));
|
||||
globals.push_back(from_ascii("\\normalcolor"));
|
||||
globals.push_back(from_ascii("\\textcolor"));
|
||||
globals.push_back(from_ascii("\\dfrac"));
|
||||
globals.push_back(from_ascii("\\tfrac"));
|
||||
globals.push_back(from_ascii("\\dbinom"));
|
||||
globals.push_back(from_ascii("\\tbinom"));
|
||||
globals.push_back(from_ascii("\\hphantom"));
|
||||
globals.push_back(from_ascii("\\phantom"));
|
||||
globals.push_back(from_ascii("\\vphantom"));
|
||||
WordList const & words = mathedWordList();
|
||||
WordList::const_iterator it2;
|
||||
lyxerr << "Globals completion commands: ";
|
||||
for (it2 = words.begin(); it2 != words.end(); ++it2) {
|
||||
globals.push_back("\\" + (*it2).first);
|
||||
lyxerr << "\\" + (*it2).first << " ";
|
||||
}
|
||||
lyxerr << std::endl;
|
||||
sort(globals.begin(), globals.end());
|
||||
}
|
||||
|
||||
|
||||
MathCompletionList::~MathCompletionList()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
size_type MathCompletionList::size() const
|
||||
{
|
||||
return favorites.size() + locals.size() + globals.size();
|
||||
}
|
||||
|
||||
|
||||
docstring MathCompletionList::data(size_t idx) const
|
||||
{
|
||||
size_t fsize = favorites.size();
|
||||
size_t lsize = locals.size();
|
||||
if (idx >= fsize + lsize)
|
||||
return globals[idx - lsize - fsize];
|
||||
else if (idx >= fsize)
|
||||
return locals[idx - fsize];
|
||||
else
|
||||
return favorites[idx];
|
||||
}
|
||||
|
||||
|
||||
std::string MathCompletionList::icon(size_t idx) const
|
||||
{
|
||||
// get the latex command
|
||||
docstring cmd;
|
||||
size_t fsize = favorites.size();
|
||||
size_t lsize = locals.size();
|
||||
if (idx >= fsize + lsize)
|
||||
cmd = globals[idx - lsize - fsize];
|
||||
else if (idx >= fsize)
|
||||
cmd = locals[idx - fsize];
|
||||
else
|
||||
cmd = favorites[idx];
|
||||
|
||||
// get the icon resource name by stripping the backslash
|
||||
return "images/math/" + to_utf8(cmd.substr(1)) + ".png";
|
||||
}
|
||||
|
||||
|
||||
|
||||
void MathCompletionList::addToFavorites(docstring const & completion)
|
||||
{
|
||||
// remove old occurrence
|
||||
std::deque<docstring>::iterator it;
|
||||
for (it = favorites.begin(); it != favorites.end(); ++it) {
|
||||
if (*it == completion) {
|
||||
favorites.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// put it to the front
|
||||
favorites.push_front(completion);
|
||||
favorites.resize(min(int(favorites.size()), 10));
|
||||
}
|
||||
|
||||
|
||||
std::vector<docstring> MathCompletionList::globals;
|
||||
std::deque<docstring> MathCompletionList::favorites;
|
||||
|
||||
} // namespace lyx
|
||||
|
@ -14,9 +14,35 @@
|
||||
|
||||
#include "InsetMath.h"
|
||||
|
||||
#include <deque>
|
||||
|
||||
namespace lyx {
|
||||
|
||||
class MathCompletionList : public Inset::CompletionList {
|
||||
public:
|
||||
///
|
||||
MathCompletionList(Cursor const & cur);
|
||||
///
|
||||
virtual ~MathCompletionList();
|
||||
|
||||
///
|
||||
virtual size_t size() const;
|
||||
///
|
||||
virtual docstring data(size_t idx) const;
|
||||
///
|
||||
virtual std::string icon(size_t idx) const;
|
||||
|
||||
///
|
||||
static void addToFavorites(docstring const & completion);
|
||||
|
||||
private:
|
||||
///
|
||||
static std::vector<docstring> globals;
|
||||
///
|
||||
std::vector<docstring> locals;
|
||||
///
|
||||
static std::deque<docstring> favorites;
|
||||
};
|
||||
|
||||
/** Abstract base class for all math objects that contain nested items.
|
||||
This is basically everything that is not a single character or a
|
||||
@ -111,6 +137,23 @@ public:
|
||||
///
|
||||
bool mouseHovered() const { return mouse_hover_; }
|
||||
|
||||
///
|
||||
bool completionSupported(Cursor const &) const;
|
||||
///
|
||||
bool inlineCompletionSupported(Cursor const & cur) const;
|
||||
///
|
||||
bool automaticInlineCompletion() const;
|
||||
///
|
||||
bool automaticPopupCompletion() const;
|
||||
///
|
||||
CompletionListPtr completionList(Cursor const & cur) const;
|
||||
///
|
||||
docstring completionPrefix(Cursor const & cur) const;
|
||||
///
|
||||
bool insertCompletion(Cursor & cur, docstring const & s, bool finished);
|
||||
///
|
||||
void completionPosAndDim(Cursor const &, int & x, int & y, Dimension & dim) const;
|
||||
|
||||
protected:
|
||||
///
|
||||
InsetMathNest(InsetMathNest const & inset);
|
||||
|
@ -32,6 +32,7 @@ public:
|
||||
void setName(docstring const & name);
|
||||
///
|
||||
docstring name() const;
|
||||
|
||||
/// identifies UnknownInsets
|
||||
InsetMathUnknown const * asUnknownInset() const { return this; }
|
||||
/// identifies UnknownInsets
|
||||
|
@ -205,6 +205,13 @@ void MacroTable::insert(docstring const & def, string const & requires)
|
||||
}
|
||||
|
||||
|
||||
void MacroTable::getMacroNames(std::set<docstring> & names) const
|
||||
{
|
||||
for (const_iterator it = begin(); it != end(); ++it)
|
||||
names.insert(it->first);
|
||||
}
|
||||
|
||||
|
||||
void MacroTable::dump()
|
||||
{
|
||||
lyxerr << "\n------------------------------------------" << endl;
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "support/types.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace lyx {
|
||||
@ -28,13 +29,11 @@ class MathData;
|
||||
class MathMacroTemplate;
|
||||
class Paragraph;
|
||||
|
||||
|
||||
enum MacroType {
|
||||
MacroTypeNewcommand,
|
||||
MacroTypeDef
|
||||
};
|
||||
|
||||
|
||||
///
|
||||
class MacroData {
|
||||
public:
|
||||
@ -154,6 +153,8 @@ public:
|
||||
MacroData const * get(docstring const & name) const;
|
||||
///
|
||||
void dump();
|
||||
///
|
||||
void getMacroNames(std::set<docstring> & names) const;
|
||||
|
||||
/// the global list
|
||||
static MacroTable & globalMacros();
|
||||
|
@ -28,6 +28,8 @@
|
||||
#include "CoordCache.h"
|
||||
#include "Cursor.h"
|
||||
|
||||
#include "mathed/InsetMathUnknown.h"
|
||||
|
||||
#include "support/debug.h"
|
||||
#include "support/docstream.h"
|
||||
|
||||
@ -252,17 +254,33 @@ void MathData::metrics(MetricsInfo & mi, Dimension & dim) const
|
||||
Cursor & cur = mi.base.bv->cursor();
|
||||
const_cast<MathData*>(this)->updateMacros(&cur, mi.macrocontext);
|
||||
|
||||
DocIterator const & inlineCompletionPos = mi.base.bv->inlineCompletionPos();
|
||||
MathData const * inlineCompletionData = 0;
|
||||
if (inlineCompletionPos.inMathed())
|
||||
inlineCompletionData = &inlineCompletionPos.cell();
|
||||
|
||||
dim.asc = 0;
|
||||
dim.wid = 0;
|
||||
Dimension d;
|
||||
CoordCacheBase<Inset> & coords = mi.base.bv->coordCache().insets();
|
||||
for (size_t i = 0, n = size(); i != n; ++i) {
|
||||
for (pos_type i = 0, n = size(); i != n; ++i) {
|
||||
MathAtom const & at = operator[](i);
|
||||
at->metrics(mi, d);
|
||||
coords.add(at.nucleus(), d);
|
||||
dim += d;
|
||||
if (i == n - 1)
|
||||
kerning_ = at->kerning(mi.base.bv);
|
||||
|
||||
// HACK to draw completion suggestion inline
|
||||
if (inlineCompletionData != this
|
||||
|| size_t(inlineCompletionPos.pos()) != i + 1)
|
||||
continue;
|
||||
|
||||
docstring const & completion = mi.base.bv->inlineCompletion();
|
||||
if (completion.length() == 0)
|
||||
continue;
|
||||
|
||||
dim.wid += mathed_string_width(mi.base.font, completion);
|
||||
}
|
||||
// Cache the dimension.
|
||||
mi.base.bv->coordCache().arrays().add(this, dim);
|
||||
@ -289,6 +307,11 @@ void MathData::draw(PainterInfo & pi, int x, int y) const
|
||||
|| x >= bv. workWidth())
|
||||
return;
|
||||
|
||||
DocIterator const & inlineCompletionPos = bv.inlineCompletionPos();
|
||||
MathData const * inlineCompletionData = 0;
|
||||
if (inlineCompletionPos.inMathed())
|
||||
inlineCompletionData = &inlineCompletionPos.cell();
|
||||
|
||||
CoordCacheBase<Inset> & coords = pi.base.bv->coordCache().insets();
|
||||
for (size_t i = 0, n = size(); i != n; ++i) {
|
||||
MathAtom const & at = operator[](i);
|
||||
@ -296,6 +319,34 @@ void MathData::draw(PainterInfo & pi, int x, int y) const
|
||||
at->drawSelection(pi, x, y);
|
||||
at->draw(pi, x, y);
|
||||
x += coords.dim(at.nucleus()).wid;
|
||||
|
||||
// Is the inline completion here?
|
||||
if (inlineCompletionData != this
|
||||
|| size_t(inlineCompletionPos.pos()) != i + 1)
|
||||
continue;
|
||||
docstring const & completion = bv.inlineCompletion();
|
||||
if (completion.length() == 0)
|
||||
continue;
|
||||
FontInfo f = pi.base.font;
|
||||
|
||||
// draw the unique and the non-unique completion part
|
||||
// Note: this is not time-critical as it is
|
||||
// only done once per screen.
|
||||
size_t uniqueTo = bv.inlineCompletionUniqueChars();
|
||||
docstring s1 = completion.substr(0, uniqueTo);
|
||||
docstring s2 = completion.substr(uniqueTo);
|
||||
|
||||
if (s1.size() > 0) {
|
||||
f.setColor(Color_inlinecompletion);
|
||||
pi.pain.text(x, y, s1, f);
|
||||
x += mathed_string_width(f, s1);
|
||||
}
|
||||
|
||||
if (s2.size() > 0) {
|
||||
f.setColor(Color_nonunique_inlinecompletion);
|
||||
pi.pain.text(x, y, s2, f);
|
||||
x += mathed_string_width(f, s2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,9 +69,6 @@ bool has_math_fonts;
|
||||
|
||||
namespace {
|
||||
|
||||
// file scope
|
||||
typedef map<docstring, latexkeys> WordList;
|
||||
|
||||
WordList theWordList;
|
||||
|
||||
|
||||
@ -221,6 +218,11 @@ void initSymbols()
|
||||
|
||||
} // namespace anon
|
||||
|
||||
WordList const & mathedWordList()
|
||||
{
|
||||
return theWordList;
|
||||
}
|
||||
|
||||
|
||||
void initMath()
|
||||
{
|
||||
|
@ -12,8 +12,14 @@
|
||||
#ifndef MATH_FACTORY_H
|
||||
#define MATH_FACTORY_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "MathParser.h"
|
||||
|
||||
#include "support/strfwd.h"
|
||||
|
||||
using std::map;
|
||||
|
||||
namespace lyx {
|
||||
|
||||
class MathAtom;
|
||||
@ -29,6 +35,8 @@ MathAtom createInsetMath(char const * const);
|
||||
*/
|
||||
bool createInsetMath_fromDialogStr(docstring const &, MathData &);
|
||||
|
||||
typedef map<docstring, latexkeys> WordList;
|
||||
WordList const & mathedWordList();
|
||||
|
||||
} // namespace lyx
|
||||
|
||||
|
@ -21,10 +21,11 @@
|
||||
#include "BufferView.h"
|
||||
#include "CoordCache.h"
|
||||
#include "Cursor.h"
|
||||
#include "LaTeXFeatures.h"
|
||||
#include "LyXRC.h"
|
||||
#include "FuncStatus.h"
|
||||
#include "FuncRequest.h"
|
||||
#include "LaTeXFeatures.h"
|
||||
#include "LyXFunc.h"
|
||||
#include "LyXRC.h"
|
||||
#include "Undo.h"
|
||||
|
||||
#include "frontends/Painter.h"
|
||||
@ -722,4 +723,88 @@ void MathMacro::infoize2(odocstream & os) const
|
||||
}
|
||||
|
||||
|
||||
bool MathMacro::completionSupported(Cursor const & cur) const
|
||||
{
|
||||
return lyxrc.completion_popup_math
|
||||
&& displayMode() == DISPLAY_UNFOLDED
|
||||
&& cur.bv().cursor().pos() == int(name().size());
|
||||
}
|
||||
|
||||
|
||||
bool MathMacro::inlineCompletionSupported(Cursor const & cur) const
|
||||
{
|
||||
return lyxrc.completion_inline_math
|
||||
&& displayMode() == DISPLAY_UNFOLDED
|
||||
&& cur.bv().cursor().pos() == int(name().size());
|
||||
}
|
||||
|
||||
|
||||
bool MathMacro::automaticInlineCompletion() const
|
||||
{
|
||||
return lyxrc.completion_inline_math;
|
||||
}
|
||||
|
||||
|
||||
bool MathMacro::automaticPopupCompletion() const
|
||||
{
|
||||
return lyxrc.completion_popup_math;
|
||||
}
|
||||
|
||||
|
||||
Inset::CompletionListPtr MathMacro::completionList(Cursor const & cur) const
|
||||
{
|
||||
return CompletionListPtr(new MathCompletionList(cur.bv().cursor()));
|
||||
}
|
||||
|
||||
|
||||
docstring MathMacro::completionPrefix(Cursor const & cur) const
|
||||
{
|
||||
if (!completionSupported(cur))
|
||||
return docstring();
|
||||
|
||||
return "\\" + name();
|
||||
}
|
||||
|
||||
|
||||
bool MathMacro::insertCompletion(Cursor & cur, docstring const & s,
|
||||
bool finished)
|
||||
{
|
||||
if (completionSupported(cur))
|
||||
return false;
|
||||
|
||||
// append completion
|
||||
docstring newName = name() + s;
|
||||
asArray(newName, cell(0));
|
||||
cur.bv().cursor().pos() = name().size();
|
||||
cur.updateFlags(Update::Force);
|
||||
|
||||
// finish macro
|
||||
if (finished) {
|
||||
#if 0
|
||||
// FIXME: this creates duplicates in the completion popup
|
||||
// which looks ugly. Moreover the changes the list lengths
|
||||
// which seems to confuse the popup as well.
|
||||
MathCompletionList::addToFavorites(inset->name());
|
||||
#endif
|
||||
cur.bv().cursor().pop();
|
||||
++cur.bv().cursor().pos();
|
||||
cur.updateFlags(Update::Force | Update::SinglePar);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::completionPosAndDim(Cursor const & cur, int & x, int & y,
|
||||
Dimension & dim) const
|
||||
{
|
||||
// get inset dimensions
|
||||
dim = cur.bv().coordCache().insets().dim(this);
|
||||
Point xy
|
||||
= cur.bv().coordCache().insets().xy(this);
|
||||
x = xy.x_;
|
||||
y = xy.y_;
|
||||
}
|
||||
|
||||
|
||||
} // namespace lyx
|
||||
|
@ -175,6 +175,24 @@ private:
|
||||
std::string requires_;
|
||||
/// update macro representation
|
||||
bool needsUpdate_;
|
||||
|
||||
public:
|
||||
///
|
||||
bool completionSupported(Cursor const &) const;
|
||||
///
|
||||
bool inlineCompletionSupported(Cursor const & cur) const;
|
||||
///
|
||||
bool automaticInlineCompletion() const;
|
||||
///
|
||||
bool automaticPopupCompletion() const;
|
||||
///
|
||||
CompletionListPtr completionList(Cursor const & cur) const;
|
||||
///
|
||||
docstring completionPrefix(Cursor const & cur) const;
|
||||
///
|
||||
bool insertCompletion(Cursor & cur, docstring const & s, bool finished);
|
||||
///
|
||||
void completionPosAndDim(Cursor const &, int & x, int & y, Dimension & dim) const;
|
||||
};
|
||||
|
||||
} // namespace lyx
|
||||
|
@ -71,6 +71,17 @@ RowPainter::RowPainter(PainterInfo & pi,
|
||||
|
||||
BOOST_ASSERT(pit >= 0);
|
||||
BOOST_ASSERT(pit < int(text.paragraphs().size()));
|
||||
|
||||
// check for possible inline completion
|
||||
DocIterator const & inlineCompletionPos = pi_.base.bv->inlineCompletionPos();
|
||||
inlineCompletionVPos_ = -1;
|
||||
if (inlineCompletionPos.inTexted()
|
||||
&& inlineCompletionPos.text() == &text_
|
||||
&& inlineCompletionPos.pit() == pit_) {
|
||||
// draw visually behind the previous character
|
||||
// FIXME: probably special RTL handling needed here
|
||||
inlineCompletionVPos_ = bidi_.log2vis(inlineCompletionPos.pos() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -688,7 +699,7 @@ void RowPainter::paintText()
|
||||
// it's in the last row of a paragraph; see skipped_sep_vpos declaration
|
||||
if (end > 0 && end < par_.size() && par_.isSeparator(end - 1))
|
||||
skipped_sep_vpos = bidi_.log2vis(end - 1);
|
||||
|
||||
|
||||
for (pos_type vpos = row_.pos(); vpos < end; ) {
|
||||
if (x_ > pi_.base.bv->workWidth())
|
||||
break;
|
||||
@ -710,6 +721,11 @@ void RowPainter::paintText()
|
||||
if (vpos < font_span.first || vpos > font_span.last) {
|
||||
font_span = par_.fontSpan(vpos);
|
||||
font = text_metrics_.getDisplayFont(pit_, vpos);
|
||||
|
||||
// split font span if inline completion is inside
|
||||
if (font_span.first <= inlineCompletionVPos_
|
||||
&& font_span.last > inlineCompletionVPos_)
|
||||
font_span.last = inlineCompletionVPos_;
|
||||
}
|
||||
|
||||
const int width_pos = pm_.singleWidth(pos, font);
|
||||
@ -769,6 +785,32 @@ void RowPainter::paintText()
|
||||
// paint as many characters as possible.
|
||||
paintFromPos(vpos);
|
||||
}
|
||||
|
||||
// Is the inline completion here?
|
||||
// FIXME: RTL support needed here
|
||||
if (vpos - 1 == inlineCompletionVPos_) {
|
||||
docstring const & completion = pi_.base.bv->inlineCompletion();
|
||||
FontInfo f = font.fontInfo();
|
||||
|
||||
// draw the unique and the non-unique completion part
|
||||
// Note: this is not time-critical as it is
|
||||
// only done once per screen.
|
||||
size_t uniqueTo = pi_.base.bv->inlineCompletionUniqueChars();
|
||||
docstring s1 = completion.substr(0, uniqueTo);
|
||||
docstring s2 = completion.substr(uniqueTo);
|
||||
|
||||
if (s1.size() > 0) {
|
||||
f.setColor(Color_inlinecompletion);
|
||||
pi_.pain.text(x_, yo_, s1, f);
|
||||
x_ += theFontMetrics(font).width(s1);
|
||||
}
|
||||
|
||||
if (s2.size() > 0) {
|
||||
f.setColor(Color_nonunique_inlinecompletion);
|
||||
pi_.pain.text(x_, yo_, s2, f);
|
||||
x_ += theFontMetrics(font).width(s2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we reach the end of a struck out range, paint it
|
||||
|
@ -99,6 +99,9 @@ private:
|
||||
int const yo_; // current baseline
|
||||
double x_;
|
||||
int width_;
|
||||
|
||||
// -1 if the inline completion is not in this paragraph.
|
||||
pos_type inlineCompletionVPos_;
|
||||
};
|
||||
|
||||
} // namespace lyx
|
||||
|
Loading…
x
Reference in New Issue
Block a user