From 7382c55fd7295031928a31bed35e26c08bf343d4 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Uwe=20St=C3=B6hr?= Date: Tue, 30 Sep 2008 18:00:02 +0000 Subject: [PATCH] add support for TABs in Listings, fileformat change, fixes http://bugzilla.lyx.org/show_bug.cgi?id=3629 , patch by Vincent, lyx2lyx and UI stuff by me git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@26647 a592a061-630c-0410-9148-cb99ea01b6c8 --- development/FORMAT | 5 +- lib/lyx2lyx/lyx_1_6.py | 37 +++- src/Buffer.cpp | 9 +- src/Lexer.cpp | 4 +- src/Paragraph.cpp | 2 +- src/ParagraphMetrics.cpp | 3 + src/frontends/qt4/GuiListings.cpp | 10 + src/frontends/qt4/ui/ListingsUi.ui | 345 +++++++++++++++-------------- src/insets/InsetListings.cpp | 86 +++++++ src/rowpainter.cpp | 9 + 10 files changed, 333 insertions(+), 177 deletions(-) diff --git a/development/FORMAT b/development/FORMAT index 2611f9c50a..8453925865 100644 --- a/development/FORMAT +++ b/development/FORMAT @@ -1,6 +1,9 @@ LyX file-format changes ----------------------- +2008-09-30 Uwe Stöhr + * Format incremented to 341: support for TABs in listings". + 2008-08-01 José Matos * Format incremented to 340: move empty layouts to "Plain Layout". @@ -1023,5 +1026,3 @@ renamed as "size_kind" and "lyxsize_kind" respectively. \end_inset - - diff --git a/lib/lyx2lyx/lyx_1_6.py b/lib/lyx2lyx/lyx_1_6.py index d7c2c2043d..0577804961 100644 --- a/lib/lyx2lyx/lyx_1_6.py +++ b/lib/lyx2lyx/lyx_1_6.py @@ -2906,6 +2906,37 @@ def add_plain_layout(document): document.body[i] = "\\begin_layout Plain Layout" i += 1 + +def revert_tabulators(document): + "Revert tabulators to 4 spaces" + i = 0 + while True: + i = find_token(document.body, "\t", i) + if i == -1: + return + document.body[i] = document.body[i].replace("\t", " ") + i += 1 + + +def revert_tabsize(document): + "Revert the tabsize parameter of listings" + i = 0 + j = 0 + while True: + # either it is the only parameter + i = find_token(document.body, 'lstparams "tabsize=4"', i) + if i != -1: + del document.body[i] + # or the last one + j = find_token(document.body, "lstparams", j) + if j == -1: + return + pos = document.body[j].find(",tabsize=") + document.body[j] = document.body[j][:pos] + '"' + i += 1 + j += 1 + + ## # Conversion hub # @@ -2974,10 +3005,12 @@ convert = [[277, [fix_wrong_tables]], [337, [convert_display_enum]], [338, []], [339, []], - [340, [add_plain_layout]] + [340, [add_plain_layout]], + [341, []] ] -revert = [[339, []], +revert = [[340, [revert_tabulators, revert_tabsize]], + [339, []], [338, [revert_removed_modules]], [337, [revert_polytonicgreek]], [336, [revert_display_enum]], diff --git a/src/Buffer.cpp b/src/Buffer.cpp index 434f56103a..9db261dec7 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -115,7 +115,7 @@ namespace os = support::os; namespace { -int const LYX_FORMAT = 340; //jamatos: add plain layout +int const LYX_FORMAT = 341; //uwestoehr: TAB support for listings typedef map DepClean; typedef map > RefCache; @@ -619,11 +619,8 @@ void Buffer::insertStringAsLines(ParagraphList & pars, ++pos; space_inserted = true; } else { - const pos_type n = 8 - pos % 8; - for (pos_type i = 0; i < n; ++i) { - par.insertChar(pos, ' ', font, params().trackChanges); - ++pos; - } + par.insertChar(pos, *cit, font, params().trackChanges); + ++pos; space_inserted = true; } } else if (!isPrintable(*cit)) { diff --git a/src/Lexer.cpp b/src/Lexer.cpp index 108d26ba2c..9061d7c0b3 100644 --- a/src/Lexer.cpp +++ b/src/Lexer.cpp @@ -499,7 +499,7 @@ bool Lexer::Pimpl::nextToken() char cc = 0; is.get(cc); c = cc; - if (c >= ' ' && is) { + if ((c >= ' ' || c == '\t') && is) { buff.clear(); if (c == '\\') { // first char == '\\' @@ -513,7 +513,7 @@ bool Lexer::Pimpl::nextToken() buff.push_back(c); is.get(cc); c = cc; - } while (c >= ' ' && c != '\\' && is); + } while ((c >= ' ' || c == '\t') && c != '\\' && is); } if (c == '\\') diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp index 5f138fead8..ba3ee8e0d8 100644 --- a/src/Paragraph.cpp +++ b/src/Paragraph.cpp @@ -2396,7 +2396,7 @@ docstring Paragraph::asString(pos_type beg, pos_type end, int options) const for (pos_type i = beg; i < end; ++i) { char_type const c = d->text_[i]; - if (isPrintable(c)) + if (isPrintable(c) || c == '\t') os.put(c); else if (c == META_INSET && options & AS_STR_INSETS) getInset(i)->textString(os); diff --git a/src/ParagraphMetrics.cpp b/src/ParagraphMetrics.cpp index 1e4410644c..15ed5f5d02 100644 --- a/src/ParagraphMetrics.cpp +++ b/src/ParagraphMetrics.cpp @@ -220,6 +220,9 @@ int ParagraphMetrics::singleWidth(pos_type pos, Font const & font) const char_type c = par_->getChar(pos); + if (c == '\t') + return 4 * theFontMetrics(font).width(' '); + if (!isPrintable(c)) return theFontMetrics(font).width(c); diff --git a/src/frontends/qt4/GuiListings.cpp b/src/frontends/qt4/GuiListings.cpp index 38077d434c..7ce3c39888 100644 --- a/src/frontends/qt4/GuiListings.cpp +++ b/src/frontends/qt4/GuiListings.cpp @@ -20,6 +20,7 @@ #include "insets/InsetListings.h" #include "insets/InsetListingsParams.h" +#include "support/convert.h" #include "support/debug.h" #include "support/gettext.h" #include "support/lstrings.h" @@ -198,6 +199,8 @@ GuiListings::GuiListings(GuiView & lv) this, SLOT(change_adaptor())); connect(spaceInStringCB, SIGNAL(clicked()), this, SLOT(change_adaptor())); + connect(tabsizeSB, SIGNAL(valueChanged(int)), + this, SLOT(change_adaptor())); connect(extendedcharsCB, SIGNAL(clicked()), this, SLOT(change_adaptor())); @@ -296,6 +299,7 @@ string GuiListings::construct_params() basicstyle += "\\" + fontstyle; bool breakline = breaklinesCB->isChecked(); bool space = spaceCB->isChecked(); + int tabsize = tabsizeSB->value(); bool spaceInString = spaceInStringCB->isChecked(); bool extendedchars = extendedcharsCB->isChecked(); string extra = fromqstr(listingsED->toPlainText()); @@ -331,6 +335,8 @@ string GuiListings::construct_params() par.addParam("showspaces", "true"); if (!spaceInString) par.addParam("showstringspaces", "false"); + if (tabsize != 8) + par.addParam("tabsize", convert(tabsize)); if (extendedchars) par.addParam("extendedchars", "true"); par.addParams(extra); @@ -456,6 +462,7 @@ void GuiListings::updateContents() breaklinesCB->setChecked(false); spaceCB->setChecked(false); spaceInStringCB->setChecked(true); + tabsizeSB->setValue(8); extendedcharsCB->setChecked(false); // set values from param string @@ -583,6 +590,9 @@ void GuiListings::updateContents() } else if (prefixIs(*it, "showstringspaces=")) { spaceInStringCB->setChecked(contains(*it, "true")); *it = ""; + } else if (prefixIs(*it, "tabsize=")) { + tabsizeSB->setValue(convert(plainParam(it->substr(8)))); + *it = ""; } else if (prefixIs(*it, "extendedchars=")) { extendedcharsCB->setChecked(contains(*it, "true")); *it = ""; diff --git a/src/frontends/qt4/ui/ListingsUi.ui b/src/frontends/qt4/ui/ListingsUi.ui index cd7075bfcb..3ec370663b 100644 --- a/src/frontends/qt4/ui/ListingsUi.ui +++ b/src/frontends/qt4/ui/ListingsUi.ui @@ -8,8 +8,8 @@ 0 0 - 612 - 317 + 654 + 349 @@ -28,9 +28,7 @@ - - 3 - 3 + 0 0 @@ -49,150 +47,6 @@ 6 - - - - Style - - - - 9 - - - 6 - - - - - - 7 - 0 - 0 - 0 - - - - The content's base font size - - - false - - - true - - - false - - - - - - - F&ont size: - - - false - - - fontsizeCO - - - - - - - - 7 - 0 - 0 - 0 - - - - The content's base font style - - - false - - - true - - - false - - - - - - - Font Famil&y: - - - false - - - fontstyleCO - - - - - - - true - - - Use extended character table - - - &Extended character table - - - 276824133 - - - - - - - Make spaces in strings visible by a special symbol - - - Space i&n string as symbol - - - 276824147 - - - - - - - Make spaces visible by a special symbol - - - S&pace as symbol - - - 276824147 - - - - - - - Break lines longer than the linewidth - - - &Break long lines - - - 276824130 - - - - - - @@ -285,9 +139,7 @@ - - 7 - 0 + 0 0 @@ -478,6 +330,175 @@ + numberfontsizeL_2 + lastlineLE + firstlineLE + numberfontsizeL_3 + basicstyleGB + + + + + + Style + + + + QLayout::SetDefaultConstraint + + + 9 + + + 6 + + + + + F&ont size: + + + false + + + fontsizeCO + + + + + + + + 0 + 0 + + + + The content's base font size + + + false + + + true + + + false + + + + + + + Font Famil&y: + + + false + + + fontstyleCO + + + + + + + + 0 + 0 + + + + The content's base font style + + + false + + + true + + + false + + + + + + + Break lines longer than the linewidth + + + &Break long lines + + + 276824130 + + + + + + + Make spaces visible by a special symbol + + + S&pace as symbol + + + 276824147 + + + + + + + Make spaces in strings visible by a special symbol + + + Space i&n string as symbol + + + 276824147 + + + + + + + Tabulator size + + + + + + + + + + true + + + Use extended character table + + + &Extended character table + + + 276824133 + + + + + fontsizeCO + fontsize_label_3 + fontstyleCO + fontstyle_label_3 + extendedcharsCB + spaceCB + breaklinesCB + label_2 + tabsizeL + tabsizeSB + horizontalSpacer @@ -496,9 +517,7 @@ - - 3 - 3 + 0 0 @@ -535,8 +554,8 @@ 16777215 - - 0 + + ArrowCursor false @@ -556,9 +575,7 @@ - - 3 - 3 + 0 0 @@ -578,12 +595,12 @@ - - 0 - 6 + + 0 + @@ -592,7 +609,7 @@ QSizePolicy::Expanding - + 221 27 diff --git a/src/insets/InsetListings.cpp b/src/insets/InsetListings.cpp index 38dc99c1e0..95f98a5b56 100644 --- a/src/insets/InsetListings.cpp +++ b/src/insets/InsetListings.cpp @@ -33,6 +33,7 @@ #include "support/docstream.h" #include "support/gettext.h" #include "support/lstrings.h" +#include "support/lassert.h" #include "frontends/alert.h" #include "frontends/Application.h" @@ -289,6 +290,87 @@ void InsetListings::doDispatch(Cursor & cur, FuncRequest & cmd) case LFUN_INSET_DIALOG_UPDATE: cur.bv().updateDialog("listings", params2string(params())); break; + case LFUN_CELL_FORWARD: + if (cur.selection()) { + // If there is a selection, a tab is inserted at the + // beginning of each paragraph. + cur.recordUndoSelection(); + pit_type const pit_end = cur.selEnd().pit(); + for (pit_type pit = cur.selBegin().pit(); pit <= pit_end; pit++) { + LASSERT(pit < paragraphs().size(), /**/); + paragraphs()[pit].insertChar(0, '\t', + buffer().params().trackChanges); + // Update the selection pos to make sure the selection does not + // change as the inserted tab will increase the logical pos. + if (cur.anchor_.pit() == pit) + cur.anchor_.forwardPos(); + if (cur.pit() == pit) + cur.forwardPos(); + } + cur.finishUndo(); + } else { + // Maybe we shouldn't allow tabs within a line, because they + // are not (yet) aligned as one might do expect. + cur.recordUndo(); + cur.insert(from_ascii("\t")); + cur.finishUndo(); + } + break; + case LFUN_CELL_BACKWARD: + if (cur.selection()) { + // If there is a selection, a tab (if present) is removed from + // the beginning of each paragraph. + cur.recordUndoSelection(); + pit_type const pit_end = cur.selEnd().pit(); + for (pit_type pit = cur.selBegin().pit(); pit <= pit_end; pit++) { + LASSERT( pit < paragraphs().size(), /**/ ); + Paragraph & par = paragraphs()[pit]; + if (par.getChar(0) == '\t') { + if (cur.pit() == pit) + cur.posBackward(); + if (cur.anchor_.pit() == pit && cur.anchor_.pos() > 0 ) + cur.anchor_.backwardPos(); + + par.eraseChar(0, buffer().params().trackChanges); + } else + // If no tab was present, try to remove up to four spaces. + for (int n_spaces = 0; + par.getChar(0) == ' ' && n_spaces < 4; ++n_spaces) { + if (cur.pit() == pit) + cur.posBackward(); + if (cur.anchor_.pit() == pit && cur.anchor_.pos() > 0 ) + cur.anchor_.backwardPos(); + + par.eraseChar(0, buffer().params().trackChanges); + } + } + cur.finishUndo(); + } else { + // If there is no selection, try to remove a tab or some spaces + // before the position of the cursor. + LASSERT(cur.pit() >= 0 && cur.pit() < paragraphs().size(), /**/); + + Paragraph & par = paragraphs()[cur.pit()]; + pos_type const pos = cur.pos(); + + if (pos == 0) + break; + + char_type const c = par.getChar(pos - 1); + cur.recordUndo(); + if (c == '\t') { + cur.posBackward(); + par.eraseChar(cur.pos(), buffer().params().trackChanges); + } else + for (int n_spaces = 0; cur.pos() > 0 + && par.getChar(cur.pos() - 1) == ' ' && n_spaces < 4; + ++n_spaces) { + cur.posBackward(); + par.eraseChar(cur.pos(), buffer().params().trackChanges); + } + cur.finishUndo(); + } + break; default: InsetCollapsable::doDispatch(cur, cmd); break; @@ -307,6 +389,10 @@ bool InsetListings::getStatus(Cursor & cur, FuncRequest const & cmd, case LFUN_CAPTION_INSERT: status.setEnabled(!params().isInline()); return true; + case LFUN_CELL_BACKWARD: + case LFUN_CELL_FORWARD: + status.setEnabled(true); + return true; default: return InsetCollapsable::getStatus(cur, cmd, status); } diff --git a/src/rowpainter.cpp b/src/rowpainter.cpp index d36916126f..0a097ed9f2 100644 --- a/src/rowpainter.cpp +++ b/src/rowpainter.cpp @@ -246,6 +246,7 @@ void RowPainter::paintChars(pos_type & vpos, FontInfo const & font, // selected text? bool const selection = pos >= row_.sel_beg && pos < row_.sel_end; + char_type prev_char = ' '; // collect as much similar chars as we can for (++vpos ; vpos < end ; ++vpos) { pos = bidi_.vis2log(vpos); @@ -264,6 +265,11 @@ void RowPainter::paintChars(pos_type & vpos, FontInfo const & font, char_type c = par_.getChar(pos); + if (c == '\t' || prev_char == '\t') { + prev_char = c; + break; + } + if (!isPrintableNonspace(c)) break; @@ -308,6 +314,9 @@ void RowPainter::paintChars(pos_type & vpos, FontInfo const & font, docstring s(&str[0], str.size()); + if (s[0] == '\t') + s.replace(0,1,from_ascii(" ")); + if (!selection && !change_running.changed()) { x_ += pi_.pain.text(int(x_), yo_, s, font); return; -- 2.39.5