diff --git a/src/printing/printpainter.cpp b/src/printing/printpainter.cpp index 0234a6e3..1406bb82 100644 --- a/src/printing/printpainter.cpp +++ b/src/printing/printpainter.cpp @@ -1,710 +1,710 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2002-2010 Anders Lund * * Rewritten based on code of Copyright (c) 2002 Michael Goffioul * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "printpainter.h" #include "katetextfolding.h" #include "katedocument.h" #include "katebuffer.h" #include "kateview.h" #include "katehighlight.h" #include "katepartdebug.h" #include "katetextlayout.h" #include #include #include #include using namespace KatePrinter; class KatePrinter::PageLayout { public: PageLayout() : headerTagList() , footerTagList() , selectionRange() {} uint pageWidth = 0; uint pageHeight = 0; uint headerWidth = 0; uint maxWidth = 0; uint maxHeight = 0; int xstart = 0; // beginning point for painting lines int innerMargin = 0; bool selectionOnly = false; uint firstline = 0; uint lastline = 0; // Header/Footer Page uint headerHeight = 0; QStringList headerTagList; uint footerHeight = 0; QStringList footerTagList; KTextEditor::Range selectionRange; }; PrintPainter::PrintPainter(KTextEditor::DocumentPrivate *doc, KTextEditor::ViewPrivate *view) : m_view(view) , m_doc(doc) , m_printGuide(false) , m_printLineNumbers(false) , m_useHeader(false) , m_useFooter(false) , m_useBackground(false) , m_useBox(false) , m_useHeaderBackground(false) , m_useFooterBackground(false) , m_boxMargin(0) , m_boxWidth(1) , m_boxColor(Qt::black) , m_headerBackground(Qt::lightGray) , m_headerForeground(Qt::black) , m_footerBackground(Qt::lightGray) , m_footerForeground(Qt::black) , m_fhFont() , m_headerFormat() , m_footerFormat() { m_folding = new Kate::TextFolding(m_doc->buffer()); m_renderer = new KateRenderer(m_doc, *m_folding, m_view); m_renderer->setPrinterFriendly(true); updateCache(); } PrintPainter::~PrintPainter() { delete m_renderer; delete m_folding; } void PrintPainter::setUseBox(const bool on) { m_useBox = on; setBoxWidth(m_boxWidth); // reset the width } void PrintPainter::setBoxWidth(const int width) { if (m_useBox) { m_boxWidth = width; if (width < 1) { m_boxWidth = 1; } } else { m_boxWidth = 0; } } void PrintPainter::setBoxColor(const QColor &color) { if (color.isValid()) { m_boxColor = color; } } void PrintPainter::setHeaderBackground(const QColor &color) { if (color.isValid()) { m_headerBackground = color; } } void PrintPainter::setHeaderForeground(const QColor &color) { if (color.isValid()) { m_headerForeground = color; } } void PrintPainter::setFooterBackground(const QColor &color) { if (color.isValid()) { m_footerBackground = color; } } void PrintPainter::setFooterForeground(const QColor &color) { if (color.isValid()) { m_footerForeground = color; } } void PrintPainter::setColorScheme(const QString &scheme) { // directly set that for the renderer m_renderer->config()->setSchema(scheme); // changed renderer requires cache updates updateCache(); } void PrintPainter::updateCache() { m_fontHeight = m_renderer->fontHeight(); // figure out the horiizontal space required QString s = QStringLiteral("%1 ").arg(m_doc->lines()); s.fill(QLatin1Char('5'), -1); // some non-fixed fonts haven't equally wide numbers // FIXME calculate which is actually the widest... m_lineNumberWidth = m_renderer->currentFontMetrics().width(s); } void PrintPainter::paint(QPrinter *printer) const { QPainter painter(printer); PageLayout pl; configure(printer, pl); uint lineCount = pl.firstline; uint y = 0; uint currentPage = (printer->fromPage() == 0) ? 1 : printer->fromPage(); bool pageStarted = true; // On to draw something :-) while (lineCount <= pl.lastline) { if (y + m_fontHeight > pl.maxHeight) { if ((int)currentPage == printer->toPage()) { // we've reached the page break of last page to be printed break; } printer->newPage(); painter.resetTransform(); currentPage++; pageStarted = true; y = 0; } if (pageStarted) { qCDebug(LOG_KTE) << "Starting new page," << lineCount << "lines up to now."; paintNewPage(painter, currentPage, y, pl); pageStarted = false; painter.translate(pl.xstart, y); } if (m_printLineNumbers /*&& ! startCol*/) { // don't repeat! paintLineNumber(painter, lineCount, pl); } uint remainder = 0; paintLine(painter, lineCount, y, remainder, pl); if (!remainder) { lineCount++; } } painter.end(); } void PrintPainter::configure(const QPrinter *printer, PageLayout &pl) const { pl.pageHeight = printer->height(); pl.pageWidth = printer->width(); pl.headerWidth = printer->width(); pl.innerMargin = m_useBox ? m_boxMargin : 6; pl.maxWidth = printer->width(); pl.maxHeight = (m_useBox ? printer->height() - pl.innerMargin : printer->height()); pl.selectionOnly = (printer->printRange() == QPrinter::Selection); pl.lastline = m_doc->lastLine(); if (m_view && pl.selectionOnly) { // set a line range from the first selected line to the last pl.selectionRange = m_view->selectionRange(); pl.firstline = pl.selectionRange.start().line(); pl.lastline = pl.selectionRange.end().line(); } if (m_printLineNumbers) { // a small space between the line numbers and the text int _adj = m_renderer->currentFontMetrics().width(QStringLiteral("5")); // adjust available width and set horizontal start point for data pl.maxWidth -= m_lineNumberWidth + _adj; pl.xstart += m_lineNumberWidth + _adj; } if (m_useHeader || m_useFooter) { // Set up a tag map // This retrieves all tags, ued or not, but // none of theese operations should be expensive, // and searcing each tag in the format strings is avoided. QDateTime dt = QDateTime::currentDateTime(); QMap tags; KUser u(KUser::UseRealUserID); tags[QStringLiteral("u")] = u.loginName(); tags[QStringLiteral("d")] = QLocale().toString(dt, QLocale::ShortFormat); tags[QStringLiteral("D")] = QLocale().toString(dt, QLocale::LongFormat); tags[QStringLiteral("h")] = QLocale().toString(dt.time(), QLocale::ShortFormat); tags[QStringLiteral("y")] = QLocale().toString(dt.date(), QLocale::ShortFormat); tags[QStringLiteral("Y")] = QLocale().toString(dt.date(), QLocale::LongFormat); tags[QStringLiteral("f")] = m_doc->url().fileName(); tags[QStringLiteral("U")] = m_doc->url().toString(); if (pl.selectionOnly) { QString s(i18n("(Selection of) ")); tags[QStringLiteral("f")].prepend(s); tags[QStringLiteral("U")].prepend(s); } QRegularExpression reTags(QStringLiteral("%([dDfUhuyY])")); // TODO check for "%%" if (m_useHeader) { pl.headerHeight = QFontMetrics(m_fhFont).height(); if (m_useBox || m_useHeaderBackground) { pl.headerHeight += pl.innerMargin * 2; } else { pl.headerHeight += 1 + QFontMetrics(m_fhFont).leading(); } pl.headerTagList = m_headerFormat; QMutableStringListIterator it(pl.headerTagList); while (it.hasNext()) { QString tag = it.next(); QRegularExpressionMatch match; int pos = tag.indexOf(reTags, 0, &match); QString rep; while (pos > -1) { rep = tags[match.captured(1)]; tag.replace((uint)pos, 2, rep); pos += rep.length(); pos = tag.indexOf(reTags, pos, &match); } it.setValue(tag); } } if (m_useFooter) { pl.footerHeight = QFontMetrics(m_fhFont).height(); if (m_useBox || m_useFooterBackground) { pl.footerHeight += 2 * pl.innerMargin; } else { pl.footerHeight += 1; // line only } pl.footerTagList = m_footerFormat; QMutableStringListIterator it(pl.footerTagList); while (it.hasNext()) { QString tag = it.next(); QRegularExpressionMatch match; int pos = tag.indexOf(reTags, 0, &match); QString rep; while (pos > -1) { rep = tags[match.captured(1)]; tag.replace((uint)pos, 2, rep); pos += rep.length(); pos = tag.indexOf(reTags, pos, &match); } it.setValue(tag); } pl.maxHeight -= pl.footerHeight; } } // if ( useHeader || useFooter ) if (m_useBackground) { if (!m_useBox) { pl.xstart += pl.innerMargin; pl.maxWidth -= pl.innerMargin * 2; } } if (m_useBox) { // set maxwidth to something sensible pl.maxWidth -= (m_boxWidth + pl.innerMargin) * 2; pl.xstart += m_boxWidth + pl.innerMargin; // maxheight too.. pl.maxHeight -= m_boxWidth; } int pageHeight = pl.maxHeight; if (m_useHeader) { pageHeight -= pl.headerHeight + pl.innerMargin; } if (m_useFooter) { pageHeight -= pl.footerHeight + pl.innerMargin; } const int linesPerPage = pageHeight / m_fontHeight; if (printer->fromPage() > 0) { pl.firstline = (printer->fromPage() - 1) * linesPerPage; } // now that we know the vertical amount of space needed, // it is possible to calculate the total number of pages // if needed, that is if any header/footer tag contains "%P". if (!pl.headerTagList.filter(QStringLiteral("%P")).isEmpty() || !pl.footerTagList.filter(QStringLiteral("%P")).isEmpty()) { qCDebug(LOG_KTE) << "'%P' found! calculating number of pages..."; // calculate total layouted lines in the document int totalLines = 0; // TODO: right now ignores selection printing for (unsigned int i = pl.firstline; i <= pl.lastline; ++i) { KateLineLayoutPtr rangeptr(new KateLineLayout(*m_renderer)); rangeptr->setLine(i); m_renderer->layoutLine(rangeptr, (int)pl.maxWidth, false); totalLines += rangeptr->viewLineCount(); } const int totalPages = (totalLines / linesPerPage) + ((totalLines % linesPerPage) > 0 ? 1 : 0); // TODO: add space for guide if required // if ( useGuide ) // _lt += (guideHeight + (fontHeight /2)) / fontHeight; // substitute both tag lists QString re(QStringLiteral("%P")); QStringList::Iterator it; for (it = pl.headerTagList.begin(); it != pl.headerTagList.end(); ++it) { it->replace(re, QString::number(totalPages)); } for (it = pl.footerTagList.begin(); it != pl.footerTagList.end(); ++it) { (*it).replace(re, QString::number(totalPages)); } } } void PrintPainter::paintNewPage(QPainter &painter, const uint currentPage, uint &y, const PageLayout &pl) const { if (m_useHeader) { paintHeader(painter, currentPage, y, pl); } if (m_useFooter) { paintFooter(painter, currentPage, pl); } if (m_useBackground) { paintBackground(painter, y, pl); } if (m_useBox) { paintBox(painter, y, pl); } if (m_printGuide && currentPage == 1) { paintGuide(painter, y, pl); } } void PrintPainter::paintHeader(QPainter &painter, const uint currentPage, uint &y, const PageLayout &pl) const { painter.save(); painter.setPen(QPen(m_headerForeground, 0.5)); painter.setFont(m_fhFont); if (m_useHeaderBackground) { painter.fillRect(0, 0, pl.headerWidth, pl.headerHeight, m_headerBackground); } if (pl.headerTagList.count() == 3) { int valign = (m_useBox || m_useHeaderBackground || m_useBackground) ? Qt::AlignVCenter : Qt::AlignTop; int align = valign | Qt::AlignLeft; int marg = (m_useBox || m_useHeaderBackground) ? pl.innerMargin : 0; if (m_useBox) { marg += m_boxWidth; } QString s; for (int i = 0; i < 3; i++) { s = pl.headerTagList[i]; if (s.indexOf(QLatin1String("%p")) != -1) { s.replace(QLatin1String("%p"), QString::number(currentPage)); } painter.drawText(marg, 0, pl.headerWidth - (marg * 2), pl.headerHeight, align, s); align = valign | (i == 0 ? Qt::AlignHCenter : Qt::AlignRight); } } if (!(m_useHeaderBackground || m_useBox || m_useBackground)) { // draw a 1 px (!?) line to separate header from contents painter.drawLine(0, pl.headerHeight - 1, pl.headerWidth, pl.headerHeight - 1); //y += 1; now included in headerHeight } painter.restore(); y += pl.headerHeight + pl.innerMargin; } void PrintPainter::paintFooter(QPainter &painter, const uint currentPage, const PageLayout &pl) const { painter.save(); painter.setPen(QPen(m_footerForeground, 0.5)); painter.setFont(m_fhFont); if (!(m_useFooterBackground || m_useBox || m_useBackground)) {// draw a 1 px (!?) line to separate footer from contents painter.drawLine(0, pl.pageHeight - pl.footerHeight - 1, pl.headerWidth, pl.pageHeight - pl.footerHeight - 1); } if (m_useFooterBackground) { painter.fillRect(0, pl.pageHeight - pl.footerHeight, pl.headerWidth, pl.footerHeight, m_footerBackground); } if (pl.footerTagList.count() == 3) { int align = Qt::AlignVCenter | Qt::AlignLeft; int marg = (m_useBox || m_useFooterBackground) ? pl.innerMargin : 0; if (m_useBox) { marg += m_boxWidth; } QString s; for (int i = 0; i < 3; i++) { s = pl.footerTagList[i]; if (s.indexOf(QLatin1String("%p")) != -1) { s.replace(QLatin1String("%p"), QString::number(currentPage)); } painter.drawText(marg, pl.pageHeight - pl.footerHeight, pl.headerWidth - (marg * 2), pl.footerHeight, align, s); align = Qt::AlignVCenter | (i == 0 ? Qt::AlignHCenter : Qt::AlignRight); } } painter.restore(); } void PrintPainter::paintGuide(QPainter &painter, uint &y, const PageLayout &pl) const { // FIXME - this may span more pages... // draw a box unless we have boxes, in which case we end with a box line int _ystart = y; QString _hlName = m_doc->highlight()->name(); - QList _attributes; // list of highlight attributes for the legend + QVector _attributes; // list of highlight attributes for the legend m_doc->highlight()->getKateExtendedAttributeList(m_renderer->config()->schema(), _attributes); KateAttributeList _defaultAttributes; KateHlManager::self()->getDefaults(m_renderer->config()->schema(), _defaultAttributes); QColor _defaultPen = _defaultAttributes.at(0)->foreground().color(); painter.save(); painter.setPen(_defaultPen); int _marg = 0; if (m_useBox) { _marg += (2 * m_boxWidth) + (2 * pl.innerMargin); } else { if (m_useBackground) { _marg += 2 * pl.innerMargin; } _marg += 1; y += 1 + pl.innerMargin; } // draw a title string QFont _titleFont = m_renderer->config()->font(); _titleFont.setBold(true); painter.setFont(_titleFont); QRect _r; painter.drawText(QRect(_marg, y, pl.pageWidth - (2 * _marg), pl.maxHeight - y), Qt::AlignTop | Qt::AlignHCenter, i18n("Typographical Conventions for %1", _hlName), &_r); const int _w = pl.pageWidth - (_marg * 2) - (pl.innerMargin * 2); const int _x = _marg + pl.innerMargin; y += _r.height() + pl.innerMargin; painter.drawLine(_x, y, _x + _w, y); y += 1 + pl.innerMargin; int _widest(0); foreach (const KTextEditor::Attribute::Ptr &attribute, _attributes) { _widest = qMax(QFontMetrics(attribute->font()).width(attribute->name().section(QLatin1Char(':'), 1, 1)), _widest); } const int _guideCols = _w / (_widest + pl.innerMargin); // draw attrib names using their styles const int _cw = _w / _guideCols; int _i = 0; _titleFont.setUnderline(true); QString _currentHlName; foreach (const KTextEditor::Attribute::Ptr &attribute, _attributes) { QString _hl = attribute->name().section(QLatin1Char(':'), 0, 0); QString _name = attribute->name().section(QLatin1Char(':'), 1, 1); if (_hl != _hlName && _hl != _currentHlName) { _currentHlName = _hl; if (_i % _guideCols) { y += m_fontHeight; } y += pl.innerMargin; painter.setFont(_titleFont); painter.setPen(_defaultPen); painter.drawText(_x, y, _w, m_fontHeight, Qt::AlignTop, _hl + QLatin1Char(' ') + i18n("text")); y += m_fontHeight; _i = 0; } KTextEditor::Attribute _attr = *_defaultAttributes[attribute->defaultStyle()]; _attr += *attribute; painter.setPen(_attr.foreground().color()); painter.setFont(_attr.font()); if (_attr.hasProperty(QTextFormat::BackgroundBrush)) { QRect _rect = QFontMetrics(_attr.font()).boundingRect(_name); _rect.moveTo(_x + ((_i % _guideCols)*_cw), y); painter.fillRect(_rect, _attr.background()); } painter.drawText((_x + ((_i % _guideCols) * _cw)), y, _cw, m_fontHeight, Qt::AlignTop, _name); _i++; if (_i && !(_i % _guideCols)) { y += m_fontHeight; } } if (_i % _guideCols) { y += m_fontHeight;// last row not full } // draw a box around the legend painter.setPen(_defaultPen); if (m_useBox) { painter.fillRect(0, y + pl.innerMargin, pl.headerWidth, m_boxWidth, m_boxColor); } else { _marg -= 1; painter.drawRect(_marg, _ystart, pl.pageWidth - (2 * _marg), y - _ystart + pl.innerMargin); } painter.restore(); y += (m_useBox ? m_boxWidth : 1) + (pl.innerMargin * 2); } void PrintPainter::paintBox(QPainter &painter, uint &y, const PageLayout &pl) const { painter.save(); painter.setPen(QPen(m_boxColor, m_boxWidth)); painter.drawRect(0, 0, pl.pageWidth, pl.pageHeight); if (m_useHeader) { painter.drawLine(0, pl.headerHeight, pl.headerWidth, pl.headerHeight); } else { y += pl.innerMargin; } if (m_useFooter) { // drawline is not trustable, grr. painter.fillRect(0, pl.maxHeight + pl.innerMargin, pl.headerWidth, m_boxWidth, m_boxColor); } painter.restore(); } void PrintPainter::paintBackground(QPainter &painter, const uint y, const PageLayout &pl) const { // If we have a box, or the header/footer has backgrounds, we want to paint // to the border of those. Otherwise just the contents area. int _y = y, _h = pl.maxHeight - y; if (m_useBox) { _y -= pl.innerMargin; _h += 2 * pl.innerMargin; } else { if (m_useHeaderBackground) { _y -= pl.innerMargin; _h += pl.innerMargin; } if (m_useFooterBackground) { _h += pl.innerMargin; } } painter.fillRect(0, _y, pl.pageWidth, _h, m_renderer->config()->backgroundColor()); } void PrintPainter::paintLine(QPainter &painter, const uint line, uint &y, uint &remainder, const PageLayout &pl) const { // HA! this is where we print [part of] a line ;]] KateLineLayoutPtr rangeptr(new KateLineLayout(*m_renderer)); rangeptr->setLine(line); m_renderer->layoutLine(rangeptr, (int)pl.maxWidth, false); // selectionOnly: clip non-selection parts and adjust painter position if needed int _xadjust = 0; if (pl.selectionOnly) { if (m_view && m_view->blockSelection()) { int _x = m_renderer->cursorToX(rangeptr->viewLine(0), pl.selectionRange.start()); int _x1 = m_renderer->cursorToX(rangeptr->viewLine(rangeptr->viewLineCount() - 1), pl.selectionRange.end()); _xadjust = _x; painter.translate(-_xadjust, 0); painter.setClipRegion(QRegion(_x, 0, _x1 - _x, rangeptr->viewLineCount() * m_fontHeight)); } else if (line == pl.firstline || line == pl.lastline) { QRegion region(0, 0, pl.maxWidth, rangeptr->viewLineCount() * m_fontHeight); if (line == pl.firstline) { region = region.subtracted(QRegion(0, 0, m_renderer->cursorToX(rangeptr->viewLine(0), pl.selectionRange.start()), m_fontHeight)); } if (line == pl.lastline) { int _x = m_renderer->cursorToX(rangeptr->viewLine(rangeptr->viewLineCount() - 1), pl.selectionRange.end()); region = region.subtracted(QRegion(_x, 0, pl.maxWidth - _x, m_fontHeight)); } painter.setClipRegion(region); } } // If the line is too long (too many 'viewlines') to fit the remaining vertical space, // clip and adjust the painter position as necessary int _lines = rangeptr->viewLineCount(); // number of "sublines" to paint. int proceedLines = _lines; if (remainder) { proceedLines = qMin((pl.maxHeight - y) / m_fontHeight, remainder); painter.translate(0, -(_lines - int(remainder)) * m_fontHeight + 1); painter.setClipRect(0, (_lines - int(remainder)) * m_fontHeight + 1, pl.maxWidth, proceedLines * m_fontHeight); //### drop the crosspatch in printerfriendly mode??? remainder -= proceedLines; } else if (y + m_fontHeight * _lines > pl.maxHeight) { remainder = _lines - ((pl.maxHeight - y) / m_fontHeight); painter.setClipRect(0, 0, pl.maxWidth, (_lines - int(remainder)) * m_fontHeight + 1); //### drop the crosspatch in printerfriendly mode??? } m_renderer->paintTextLine(painter, rangeptr, 0, (int)pl.maxWidth); painter.setClipping(false); painter.translate(_xadjust, (m_fontHeight * (_lines - remainder))); y += m_fontHeight * proceedLines; } void PrintPainter::paintLineNumber(QPainter &painter, const uint number, const PageLayout &pl) const { const int left = ((m_useBox || m_useBackground) ? pl.innerMargin : 0) - pl.xstart; painter.save(); painter.setFont(m_renderer->config()->font()); painter.setPen(m_renderer->config()->lineNumberColor()); painter.drawText(left, 0, m_lineNumberWidth, m_fontHeight, Qt::AlignRight, QString::number(number + 1)); painter.restore(); } //END PrintPainter diff --git a/src/render/katerenderer.h b/src/render/katerenderer.h index 0633afa5..e250608a 100644 --- a/src/render/katerenderer.h +++ b/src/render/katerenderer.h @@ -1,433 +1,433 @@ /* This file is part of the KDE libraries Copyright (C) 2007 Mirko Stocker Copyright (C) 2003-2005 Hamish Rodda Copyright (C) 2001 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 1999 Jochen Wilhelmy This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KATE_RENDERER_H__ #define __KATE_RENDERER_H__ #include #include "katetextline.h" #include "katelinelayout.h" #include #include #include #include #include namespace KTextEditor { class DocumentPrivate; } namespace KTextEditor { class ViewPrivate; } class KateRendererConfig; class KateRenderRange; namespace Kate { class TextFolding; } /** * Handles all of the work of rendering the text * (used for the views and printing) * **/ class KateRenderer { public: /** * Style of Caret * * The caret is displayed as a vertical bar (Line), a filled box * (Block), a horizontal bar (Underline), or a half-height filled * box (Half). The default is Line. * * Line Block Underline Half * * ## _ ######### _ _ * ## __| | #####| |# __| | __| | * ## / _' | ##/ _' |# / _' | / _' | * ##| (_| | #| (#| |# | (_| | #| (#| |# * ## \__,_| ##\__,_|# \__,_| ##\__,_|# * ## ######### ######### ######### */ enum caretStyles { Line, Block, Underline, Half }; /** * Constructor * @param doc document to render * @param folding folding information * @param view view which is output (0 for example for rendering to print) */ explicit KateRenderer(KTextEditor::DocumentPrivate *doc, Kate::TextFolding &folding, KTextEditor::ViewPrivate *view = nullptr); /** * Destructor */ ~KateRenderer(); /** * Returns the document to which this renderer is bound */ KTextEditor::DocumentPrivate *doc() const { return m_doc; } /** * Returns the folding info to which this renderer is bound * @return folding info */ Kate::TextFolding &folding() const { return m_folding; } /** * Returns the view to which this renderer is bound */ KTextEditor::ViewPrivate *view() const { return m_view; } /** * update the highlighting attributes * (for example after an hl change or after hl config changed) */ void updateAttributes(); /** * Determine whether the caret (text cursor) will be drawn. * @return should it be drawn? */ inline bool drawCaret() const { return m_drawCaret; } /** * Set whether the caret (text cursor) will be drawn. * @param drawCaret should caret be drawn? */ void setDrawCaret(bool drawCaret); /** * The style of the caret (text cursor) to be painted. * @return caretStyle */ inline KateRenderer::caretStyles caretStyle() const { return m_caretStyle; } /** * Set the style of caret to be painted. * @param style style to set */ void setCaretStyle(KateRenderer::caretStyles style); /** * Set a \a brush with which to override drawing of the caret. Set to QColor() to clear. */ void setCaretOverrideColor(const QColor &color); /** * @returns whether tabs should be shown (ie. a small mark * drawn to identify a tab) * @return tabs should be shown */ inline bool showTabs() const { return m_showTabs; } /** * Set whether a mark should be painted to help identifying tabs. * @param showTabs show the tabs? */ void setShowTabs(bool showTabs); /** * @returns whether trailing spaces should be shown. */ inline bool showTrailingSpaces() const { return m_showSpaces; } /** * Set whether a mark should be painted for trailing spaces. */ void setShowTrailingSpaces(bool showSpaces); /** * Update marker size shown. */ void updateMarkerSize(); /** * @returns whether non-printable spaces should be shown */ inline bool showNonPrintableSpaces() const { return m_showNonPrintableSpaces; } /** * Set whether box should be drawn around non-printable spaces */ void setShowNonPrintableSpaces(const bool showNonPrintableSpaces); /** * Sets the width of the tab. Helps performance. * @param tabWidth new tab width */ void setTabWidth(int tabWidth); /** * @returns whether indent lines should be shown * @return indent lines should be shown */ bool showIndentLines() const; /** * Set whether a guide should be painted to help identifying indent lines. * @param showLines show the indent lines? */ void setShowIndentLines(bool showLines); /** * Sets the width of the tab. Helps performance. * @param indentWidth new indent width */ void setIndentWidth(int indentWidth); /** * Show the view's selection? * @return show sels? */ inline bool showSelections() const { return m_showSelections; } /** * Set whether the view's selections should be shown. * The default is true. * @param showSelections show the selections? */ void setShowSelections(bool showSelections); /** * Change to a different font (soon to be font set?) */ void increaseFontSizes(qreal step = 1.0); void decreaseFontSizes(qreal step = 1.0); const QFont ¤tFont() const; const QFontMetricsF ¤tFontMetrics() const; /** * @return whether the renderer is configured to paint in a * printer-friendly fashion. */ bool isPrinterFriendly() const; /** * Configure this renderer to paint in a printer-friendly fashion. * * Sets the other options appropriately if true. */ void setPrinterFriendly(bool printerFriendly); /** * Text width & height calculation functions... */ void layoutLine(KateLineLayoutPtr line, int maxwidth = -1, bool cacheLayout = false) const; /** * This is a smaller QString::isRightToLeft(). It's also marked as internal to kate * instead of internal to Qt, so we can modify. This method searches for the first * strong character in the paragraph and then returns its direction. In case of a * line without any strong characters, the direction is forced to be LTR. * * Back in KDE 4.1 this method counted chars, which lead to unwanted side effects. * (see https://bugs.kde.org/show_bug.cgi?id=178594). As this function is internal * the way it work will probably change between releases. Be warned! */ bool isLineRightToLeft(KateLineLayoutPtr lineLayout) const; /** * The ultimate decoration creation function. * * \param selectionsOnly return decorations for selections and/or dynamic highlighting. */ QVector decorationsForLine(const Kate::TextLine &textLine, int line, bool selectionsOnly = false, KateRenderRange *completionHighlight = nullptr, bool completionSelected = false) const; // Width calculators qreal spaceWidth() const; /** * Returns the x position of cursor \p col on the line \p range. */ int cursorToX(const KateTextLayout &range, int col, bool returnPastLine = false) const; /// \overload int cursorToX(const KateTextLayout &range, const KTextEditor::Cursor &pos, bool returnPastLine = false) const; /** * Returns the real cursor which is occupied by the specified x value, or that closest to it. * If \p returnPastLine is true, the column will be extrapolated out with the assumption * that the extra characters are spaces. */ KTextEditor::Cursor xToCursor(const KateTextLayout &range, int x, bool returnPastLine = false) const; // Font height uint fontHeight() const; // Line height int lineHeight() const; // Document height uint documentHeight() const; // Selection boundaries bool getSelectionBounds(int line, int lineLength, int &start, int &end) const; /** * Flags to customize the paintTextLine function behavior */ enum PaintTextLineFlag { /** * Skip drawing the dashed underline at the start of a folded block of text? */ SkipDrawFirstInvisibleLineUnderlined = 0x1, }; Q_DECLARE_FLAGS(PaintTextLineFlags, PaintTextLineFlag) /** * This is the ultimate function to perform painting of a text line. * * The text line is painted from the upper limit of (0,0). To move that, * apply a transform to your painter. * * @param paint painter to use * @param range layout to use in painting this line * @param xStart starting width in pixels. * @param xEnd ending width in pixels. * @param cursor position of the caret, if placed on the current line. * @param flags flags for customizing the drawing of the line */ void paintTextLine(QPainter &paint, KateLineLayoutPtr range, int xStart, int xEnd, const KTextEditor::Cursor *cursor = nullptr, PaintTextLineFlags flags = PaintTextLineFlags()); /** * Paint the background of a line * * Split off from the main @ref paintTextLine method to make it smaller. As it's being * called only once per line it shouldn't noticably affect performance and it * helps readability a LOT. * * @param paint painter to use * @param layout layout to use in painting this line * @param currentViewLine if one of the view lines is the current line, set * this to the index; otherwise -1. * @param xStart starting width in pixels. * @param xEnd ending width in pixels. */ void paintTextLineBackground(QPainter &paint, KateLineLayoutPtr layout, int currentViewLine, int xStart, int xEnd); /** * This takes an in index, and returns all the attributes for it. * For example, if you have a ktextline, and want the KTextEditor::Attribute * for a given position, do: * * attribute(myktextline->attribute(position)); */ KTextEditor::Attribute::Ptr attribute(uint pos) const; KTextEditor::Attribute::Ptr specificAttribute(int context) const; private: /** * Paint a trailing space on position (x, y). */ void paintTrailingSpace(QPainter &paint, qreal x, qreal y); /** * Paint a tab stop marker on position (x, y). */ void paintTabstop(QPainter &paint, qreal x, qreal y); /** * Paint a non-breaking space marker on position (x, y). */ void paintNonBreakSpace(QPainter &paint, qreal x, qreal y); /** * Paint non printable spaces bounding box */ void paintNonPrintableSpaces(QPainter &paint, qreal x, qreal y, const QChar &chr); /** Paint a SciTE-like indent marker. */ void paintIndentMarker(QPainter &paint, uint x, uint y); void assignSelectionBrushesFromAttribute(QTextLayout::FormatRange &target, const KTextEditor::Attribute &attribute) const; // update font height void updateFontHeight(); KTextEditor::DocumentPrivate *const m_doc; Kate::TextFolding &m_folding; KTextEditor::ViewPrivate *const m_view; // cache of config values int m_tabWidth; int m_indentWidth; int m_fontHeight; // some internal flags KateRenderer::caretStyles m_caretStyle; bool m_drawCaret; bool m_showSelections; bool m_showTabs; bool m_showSpaces; float m_markerSize; bool m_showNonPrintableSpaces; bool m_printerFriendly; QColor m_caretOverrideColor; - QList m_attributes; + QVector m_attributes; /** * Configuration */ public: inline KateRendererConfig *config() const { return m_config; } void updateConfig(); private: KateRendererConfig *const m_config; }; #endif diff --git a/src/schema/kateschemaconfig.cpp b/src/schema/kateschemaconfig.cpp index 36ea3a40..000fb070 100644 --- a/src/schema/kateschemaconfig.cpp +++ b/src/schema/kateschemaconfig.cpp @@ -1,1380 +1,1380 @@ /* This file is part of the KDE libraries Copyright (C) 2007, 2008 Matthew Woehlke Copyright (C) 2001-2003 Christoph Cullmann Copyright (C) 2002, 2003 Anders Lund Copyright (C) 2012 Dominik Haumann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ //BEGIN Includes #include "kateschemaconfig.h" #include "katedocument.h" #include "kateschema.h" #include "kateconfig.h" #include "kateglobal.h" #include "kateview.h" #include "katerenderer.h" #include "katestyletreewidget.h" #include "katecolortreewidget.h" #include "katepartdebug.h" #include "katedefaultcolors.h" #include "ui_howtoimportschema.h" #include #include #include #include #include #include #include #include #include //END //BEGIN KateSchemaConfigColorTab -- 'Colors' tab KateSchemaConfigColorTab::KateSchemaConfigColorTab() { QGridLayout *l = new QGridLayout(this); setLayout(l); ui = new KateColorTreeWidget(this); QPushButton *btnUseColorScheme = new QPushButton(i18n("Use KDE Color Scheme"), this); l->addWidget(ui, 0, 0, 1, 2); l->addWidget(btnUseColorScheme, 1, 1); l->setColumnStretch(0, 1); l->setColumnStretch(1, 0); connect(btnUseColorScheme, SIGNAL(clicked()), ui, SLOT(selectDefaults())); connect(ui, SIGNAL(changed()), SIGNAL(changed())); } KateSchemaConfigColorTab::~KateSchemaConfigColorTab() { } QVector KateSchemaConfigColorTab::colorItemList() const { QVector items; // use global color instance, creation is expensive! const KateDefaultColors &colors(KTextEditor::EditorPrivate::self()->defaultColors()); // // editor background colors // KateColorItem ci; ci.category = i18n("Editor Background Colors"); ci.name = i18n("Text Area"); ci.key = QStringLiteral("Color Background"); ci.whatsThis = i18n("

Sets the background color of the editing area.

"); ci.defaultColor = colors.color(Kate::Background); items.append(ci); ci.name = i18n("Selected Text"); ci.key = QStringLiteral("Color Selection"); ci.whatsThis = i18n("

Sets the background color of the selection.

To set the text color for selected text, use the "Configure Highlighting" dialog.

"); ci.defaultColor = colors.color(Kate::SelectionBackground); items.append(ci); ci.name = i18n("Current Line"); ci.key = QStringLiteral("Color Highlighted Line"); ci.whatsThis = i18n("

Sets the background color of the currently active line, which means the line where your cursor is positioned.

"); ci.defaultColor = colors.color(Kate::HighlightedLineBackground); items.append(ci); ci.name = i18n("Search Highlight"); ci.key = QStringLiteral("Color Search Highlight"); ci.whatsThis = i18n("

Sets the background color of search results.

"); ci.defaultColor = colors.color(Kate::SearchHighlight); items.append(ci); ci.name = i18n("Replace Highlight"); ci.key = QStringLiteral("Color Replace Highlight"); ci.whatsThis = i18n("

Sets the background color of replaced text.

"); ci.defaultColor = colors.color(Kate::ReplaceHighlight); items.append(ci); // // icon border // ci.category = i18n("Icon Border"); ci.name = i18n("Background Area"); ci.key = QStringLiteral("Color Icon Bar"); ci.whatsThis = i18n("

Sets the background color of the icon border.

"); ci.defaultColor = colors.color(Kate::IconBar); items.append(ci); ci.name = i18n("Line Numbers"); ci.key = QStringLiteral("Color Line Number"); ci.whatsThis = i18n("

This color will be used to draw the line numbers (if enabled).

"); ci.defaultColor = colors.color(Kate::LineNumber); items.append(ci); ci.name = i18n("Current Line Number"); ci.key = QStringLiteral("Color Current Line Number"); ci.whatsThis = i18n("

This color will be used to draw the number of the current line (if enabled).

"); ci.defaultColor = colors.color(Kate::CurrentLineNumber); items.append(ci); ci.name = i18n("Separator"); ci.key = QStringLiteral("Color Separator"); ci.whatsThis = i18n("

This color will be used to draw the line between line numbers and the icon borders, if both are enabled.

"); ci.defaultColor = colors.color(Kate::Separator); items.append(ci); ci.name = i18n("Word Wrap Marker"); ci.key = QStringLiteral("Color Word Wrap Marker"); ci.whatsThis = i18n("

Sets the color of Word Wrap-related markers:

Static Word Wrap
A vertical line which shows the column where text is going to be wrapped
Dynamic Word Wrap
An arrow shown to the left of visually-wrapped lines
"); ci.defaultColor = colors.color(Kate::WordWrapMarker); items.append(ci); ci.name = i18n("Code Folding"); ci.key = QStringLiteral("Color Code Folding"); ci.whatsThis = i18n("

Sets the color of the code folding bar.

"); ci.defaultColor = colors.color(Kate::CodeFolding); items.append(ci); ci.name = i18n("Modified Lines"); ci.key = QStringLiteral("Color Modified Lines"); ci.whatsThis = i18n("

Sets the color of the line modification marker for modified lines.

"); ci.defaultColor = colors.color(Kate::ModifiedLine); items.append(ci); ci.name = i18n("Saved Lines"); ci.key = QStringLiteral("Color Saved Lines"); ci.whatsThis = i18n("

Sets the color of the line modification marker for saved lines.

"); ci.defaultColor = colors.color(Kate::SavedLine); items.append(ci); // // text decorations // ci.category = i18n("Text Decorations"); ci.name = i18n("Spelling Mistake Line"); ci.key = QStringLiteral("Color Spelling Mistake Line"); ci.whatsThis = i18n("

Sets the color of the line that is used to indicate spelling mistakes.

"); ci.defaultColor = colors.color(Kate::SpellingMistakeLine); items.append(ci); ci.name = i18n("Tab and Space Markers"); ci.key = QStringLiteral("Color Tab Marker"); ci.whatsThis = i18n("

Sets the color of the tabulator marks.

"); ci.defaultColor = colors.color(Kate::TabMarker); items.append(ci); ci.name = i18n("Indentation Line"); ci.key = QStringLiteral("Color Indentation Line"); ci.whatsThis = i18n("

Sets the color of the vertical indentation lines.

"); ci.defaultColor = colors.color(Kate::IndentationLine); items.append(ci); ci.name = i18n("Bracket Highlight"); ci.key = QStringLiteral("Color Highlighted Bracket"); ci.whatsThis = i18n("

Sets the bracket matching color. This means, if you place the cursor e.g. at a (, the matching ) will be highlighted with this color.

"); ci.defaultColor = colors.color(Kate::HighlightedBracket); items.append(ci); // // marker colors // ci.category = i18n("Marker Colors"); const QString markerNames[Kate::LAST_MARK + 1] = { i18n("Bookmark"), i18n("Active Breakpoint"), i18n("Reached Breakpoint"), i18n("Disabled Breakpoint"), i18n("Execution"), i18n("Warning"), i18n("Error") }; ci.whatsThis = i18n("

Sets the background color of mark type.

Note: The marker color is displayed lightly because of transparency.

"); for (int i = Kate::FIRST_MARK; i <= Kate::LAST_MARK; ++i) { ci.defaultColor = colors.mark(i); ci.name = markerNames[i]; ci.key = QLatin1String("Color MarkType ") + QString::number(i + 1); items.append(ci); } // // text templates // ci.category = i18n("Text Templates & Snippets"); ci.whatsThis = QString(); // TODO: add whatsThis for text templates ci.name = i18n("Background"); ci.key = QStringLiteral("Color Template Background"); ci.defaultColor = colors.color(Kate::TemplateBackground); items.append(ci); ci.name = i18n("Editable Placeholder"); ci.key = QStringLiteral("Color Template Editable Placeholder"); ci.defaultColor = colors.color(Kate::TemplateEditablePlaceholder); items.append(ci); ci.name = i18n("Focused Editable Placeholder"); ci.key = QStringLiteral("Color Template Focused Editable Placeholder"); ci.defaultColor = colors.color(Kate::TemplateFocusedEditablePlaceholder); items.append(ci); ci.name = i18n("Not Editable Placeholder"); ci.key = QStringLiteral("Color Template Not Editable Placeholder"); ci.defaultColor = colors.color(Kate::TemplateNotEditablePlaceholder); items.append(ci); // // finally, add all elements // return items; } void KateSchemaConfigColorTab::schemaChanged(const QString &newSchema) { // save curent schema if (!m_currentSchema.isEmpty()) { if (m_schemas.contains(m_currentSchema)) { m_schemas.remove(m_currentSchema); // clear this color schema } // now add it again m_schemas[m_currentSchema] = ui->colorItems(); } if (newSchema == m_currentSchema) { return; } // switch m_currentSchema = newSchema; // If we havent this schema, read in from config file if (! m_schemas.contains(newSchema)) { KConfigGroup config = KTextEditor::EditorPrivate::self()->schemaManager()->schema(newSchema); QVector items = readConfig(config); m_schemas[ newSchema ] = items; } // first block signals otherwise setColor emits changed const bool blocked = blockSignals(true); ui->clear(); ui->addColorItems(m_schemas[m_currentSchema]); blockSignals(blocked); } QVector KateSchemaConfigColorTab::readConfig(KConfigGroup &config) { QVector items = colorItemList(); for (int i = 0; i < items.count(); ++i) { KateColorItem &item(items[i]); item.useDefault = !config.hasKey(item.key); if (item.useDefault) { item.color = item.defaultColor; } else { item.color = config.readEntry(item.key, item.defaultColor); if (!item.color.isValid()) { config.deleteEntry(item.key); item.useDefault = true; item.color = item.defaultColor; } } } return items; } void KateSchemaConfigColorTab::importSchema(KConfigGroup &config) { m_schemas[m_currentSchema] = readConfig(config); // first block signals otherwise setColor emits changed const bool blocked = blockSignals(true); ui->clear(); ui->addColorItems(m_schemas[m_currentSchema]); blockSignals(blocked); } void KateSchemaConfigColorTab::exportSchema(KConfigGroup &config) { QVector items = ui->colorItems(); foreach (const KateColorItem &item, items) { QColor c = item.useDefault ? item.defaultColor : item.color; config.writeEntry(item.key, c); } } void KateSchemaConfigColorTab::apply() { schemaChanged(m_currentSchema); QMap >::Iterator it; for (it = m_schemas.begin(); it != m_schemas.end(); ++it) { KConfigGroup config = KTextEditor::EditorPrivate::self()->schemaManager()->schema(it.key()); foreach (const KateColorItem &item, m_schemas[it.key()]) { if (item.useDefault) { config.deleteEntry(item.key); } else { config.writeEntry(item.key, item.color); } } // add dummy entry to prevent the config group from being empty. // As if the group is empty, KateSchemaManager will not find it anymore. config.writeEntry("dummy", "prevent-empty-group"); } // all colors are written, so throw away all cached schemas m_schemas.clear(); } void KateSchemaConfigColorTab::reload() { // drop all cached data m_schemas.clear(); // load from config KConfigGroup config = KTextEditor::EditorPrivate::self()->schemaManager()->schema(m_currentSchema); QVector items = readConfig(config); // first block signals otherwise setColor emits changed const bool blocked = blockSignals(true); ui->clear(); ui->addColorItems(items); blockSignals(blocked); } QColor KateSchemaConfigColorTab::backgroundColor() const { return ui->findColor(QStringLiteral("Color Background")); } QColor KateSchemaConfigColorTab::selectionColor() const { return ui->findColor(QStringLiteral("Color Selection")); } //END KateSchemaConfigColorTab //BEGIN FontConfig -- 'Fonts' tab KateSchemaConfigFontTab::KateSchemaConfigFontTab() { QGridLayout *grid = new QGridLayout(this); m_fontchooser = new KFontChooser(this, KFontChooser::NoDisplayFlags); grid->addWidget(m_fontchooser, 0, 0); } KateSchemaConfigFontTab::~KateSchemaConfigFontTab() { } void KateSchemaConfigFontTab::slotFontSelected(const QFont &font) { if (!m_currentSchema.isEmpty()) { m_fonts[m_currentSchema] = font; emit changed(); } } void KateSchemaConfigFontTab::apply() { QMap::Iterator it; for (it = m_fonts.begin(); it != m_fonts.end(); ++it) { KTextEditor::EditorPrivate::self()->schemaManager()->schema(it.key()).writeEntry("Font", it.value()); } // all fonts are written, so throw away all cached schemas m_fonts.clear(); } void KateSchemaConfigFontTab::reload() { // drop all cached data m_fonts.clear(); // now set current schema font in the font chooser schemaChanged(m_currentSchema); } void KateSchemaConfigFontTab::schemaChanged(const QString &newSchema) { m_currentSchema = newSchema; // reuse font, if cached QFont newFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); if (m_fonts.contains(m_currentSchema)) { newFont = m_fonts[m_currentSchema]; } else { newFont = KTextEditor::EditorPrivate::self()->schemaManager()->schema(m_currentSchema).readEntry("Font", newFont); } m_fontchooser->disconnect(this); m_fontchooser->setFont(newFont); connect(m_fontchooser, SIGNAL(fontSelected(QFont)), this, SLOT(slotFontSelected(QFont))); } void KateSchemaConfigFontTab::importSchema(KConfigGroup &config) { QFont f(QFontDatabase::systemFont(QFontDatabase::FixedFont)); m_fontchooser->setFont(config.readEntry("Font", f)); m_fonts[m_currentSchema] = m_fontchooser->font(); } void KateSchemaConfigFontTab::exportSchema(KConfigGroup &config) { config.writeEntry("Font", m_fontchooser->font()); } //END FontConfig //BEGIN FontColorConfig -- 'Normal Text Styles' tab KateSchemaConfigDefaultStylesTab::KateSchemaConfigDefaultStylesTab(KateSchemaConfigColorTab *colorTab) { m_colorTab = colorTab; // size management QGridLayout *grid = new QGridLayout(this); m_defaultStyles = new KateStyleTreeWidget(this); connect(m_defaultStyles, SIGNAL(changed()), this, SIGNAL(changed())); grid->addWidget(m_defaultStyles, 0, 0); m_defaultStyles->setWhatsThis(i18n( "

This list displays the default styles for the current schema and " "offers the means to edit them. The style name reflects the current " "style settings.

" "

To edit the colors, click the colored squares, or select the color " "to edit from the popup menu.

You can unset the Background and Selected " "Background colors from the popup menu when appropriate.

")); } KateSchemaConfigDefaultStylesTab::~KateSchemaConfigDefaultStylesTab() { qDeleteAll(m_defaultStyleLists); } KateAttributeList *KateSchemaConfigDefaultStylesTab::attributeList(const QString &schema) { if (!m_defaultStyleLists.contains(schema)) { KateAttributeList *list = new KateAttributeList(); KateHlManager::self()->getDefaults(schema, *list); m_defaultStyleLists.insert(schema, list); } return m_defaultStyleLists[schema]; } void KateSchemaConfigDefaultStylesTab::schemaChanged(const QString &schema) { m_currentSchema = schema; m_defaultStyles->clear(); KateAttributeList *l = attributeList(schema); updateColorPalette(l->at(0)->foreground().color()); // normal text and source code QTreeWidgetItem *parent = new QTreeWidgetItem(m_defaultStyles, QStringList() << i18nc("@item:intable", "Normal Text & Source Code")); parent->setFirstColumnSpanned(true); for (int i = (int)KTextEditor::dsNormal; i <= (int)KTextEditor::dsAttribute; ++i) { m_defaultStyles->addItem(parent, KateHlManager::self()->defaultStyleName(i, true), l->at(i)); } // Number, Types & Constants parent = new QTreeWidgetItem(m_defaultStyles, QStringList() << i18nc("@item:intable", "Numbers, Types & Constants")); parent->setFirstColumnSpanned(true); for (int i = (int)KTextEditor::dsDataType; i <= (int)KTextEditor::dsConstant; ++i) { m_defaultStyles->addItem(parent, KateHlManager::self()->defaultStyleName(i, true), l->at(i)); } // strings & characters parent = new QTreeWidgetItem(m_defaultStyles, QStringList() << i18nc("@item:intable", "Strings & Characters")); parent->setFirstColumnSpanned(true); for (int i = (int)KTextEditor::dsChar; i <= (int)KTextEditor::dsImport; ++i) { m_defaultStyles->addItem(parent, KateHlManager::self()->defaultStyleName(i, true), l->at(i)); } // comments & documentation parent = new QTreeWidgetItem(m_defaultStyles, QStringList() << i18nc("@item:intable", "Comments & Documentation")); parent->setFirstColumnSpanned(true); for (int i = (int)KTextEditor::dsComment; i <= (int)KTextEditor::dsAlert; ++i) { m_defaultStyles->addItem(parent, KateHlManager::self()->defaultStyleName(i, true), l->at(i)); } // Misc parent = new QTreeWidgetItem(m_defaultStyles, QStringList() << i18nc("@item:intable", "Miscellaneous")); parent->setFirstColumnSpanned(true); for (int i = (int)KTextEditor::dsOthers; i <= (int)KTextEditor::dsError; ++i) { m_defaultStyles->addItem(parent, KateHlManager::self()->defaultStyleName(i, true), l->at(i)); } m_defaultStyles->expandAll(); } void KateSchemaConfigDefaultStylesTab::updateColorPalette(const QColor &textColor) { QPalette p(m_defaultStyles->palette()); p.setColor(QPalette::Base, m_colorTab->backgroundColor()); p.setColor(QPalette::Highlight, m_colorTab->selectionColor()); p.setColor(QPalette::Text, textColor); m_defaultStyles->setPalette(p); } void KateSchemaConfigDefaultStylesTab::reload() { m_defaultStyles->clear(); qDeleteAll(m_defaultStyleLists); m_defaultStyleLists.clear(); schemaChanged(m_currentSchema); } void KateSchemaConfigDefaultStylesTab::apply() { QHashIterator it = m_defaultStyleLists; while (it.hasNext()) { it.next(); KateHlManager::self()->setDefaults(it.key(), *it.value()); } } void KateSchemaConfigDefaultStylesTab::exportSchema(const QString &schema, KConfig *cfg) { KateHlManager::self()->setDefaults(schema, *(m_defaultStyleLists[schema]), cfg); } void KateSchemaConfigDefaultStylesTab::importSchema(const QString &schemaName, const QString &schema, KConfig *cfg) { KateHlManager::self()->getDefaults(schemaName, *(m_defaultStyleLists[schema]), cfg); } void KateSchemaConfigDefaultStylesTab::showEvent(QShowEvent *event) { if (!event->spontaneous() && !m_currentSchema.isEmpty()) { KateAttributeList *l = attributeList(m_currentSchema); Q_ASSERT(l != nullptr); updateColorPalette(l->at(0)->foreground().color()); } QWidget::showEvent(event); } //END FontColorConfig //BEGIN KateSchemaConfigHighlightTab -- 'Highlighting Text Styles' tab KateSchemaConfigHighlightTab::KateSchemaConfigHighlightTab(KateSchemaConfigDefaultStylesTab *page, KateSchemaConfigColorTab *colorTab) { m_defaults = page; m_colorTab = colorTab; m_hl = 0; QVBoxLayout *layout = new QVBoxLayout(this); QHBoxLayout *headerLayout = new QHBoxLayout; layout->addLayout(headerLayout); QLabel *lHl = new QLabel(i18n("H&ighlight:"), this); headerLayout->addWidget(lHl); hlCombo = new KComboBox(this); hlCombo->setEditable(false); headerLayout->addWidget(hlCombo); lHl->setBuddy(hlCombo); connect(hlCombo, SIGNAL(activated(int)), this, SLOT(hlChanged(int))); QPushButton *btnexport = new QPushButton(i18n("Export..."), this); headerLayout->addWidget(btnexport); connect(btnexport, SIGNAL(clicked()), this, SLOT(exportHl())); QPushButton *btnimport = new QPushButton(i18n("Import..."), this); headerLayout->addWidget(btnimport); connect(btnimport, SIGNAL(clicked()), this, SLOT(importHl())); headerLayout->addStretch(); for (const auto &hl : KateHlManager::self()->modeList()) { if (hl.section().length() > 0) { hlCombo->addItem(hl.section() + QLatin1String("/") + hl.translatedName()); } else { hlCombo->addItem(hl.translatedName()); } } hlCombo->setCurrentIndex(0); // styles listview m_styles = new KateStyleTreeWidget(this, true); connect(m_styles, SIGNAL(changed()), this, SIGNAL(changed())); layout->addWidget(m_styles, 999); // get current highlighting from the host application int hl = 0; KTextEditor::ViewPrivate *kv = qobject_cast(KTextEditor::EditorPrivate::self()->application()->activeMainWindow()->activeView()); if (kv) { const QString hlName = kv->doc()->highlight()->name(); hl = KateHlManager::self()->nameFind(hlName); Q_ASSERT(hl >= 0); } hlCombo->setCurrentIndex(hl); hlChanged(hl); m_styles->setWhatsThis(i18n( "

This list displays the contexts of the current syntax highlight mode and " "offers the means to edit them. The context name reflects the current " "style settings.

To edit using the keyboard, press " "<SPACE> and choose a property from the popup menu.

" "

To edit the colors, click the colored squares, or select the color " "to edit from the popup menu.

You can unset the Background and Selected " "Background colors from the context menu when appropriate.

")); } KateSchemaConfigHighlightTab::~KateSchemaConfigHighlightTab() { } void KateSchemaConfigHighlightTab::hlChanged(int z) { m_hl = z; schemaChanged(m_schema); } bool KateSchemaConfigHighlightTab::loadAllHlsForSchema(const QString &schema) { QProgressDialog progress(i18n("Loading all highlightings for schema"), QString(), 0, KateHlManager::self()->modeList().size(), this); progress.setWindowModality(Qt::WindowModal); for (int i = 0; i < KateHlManager::self()->modeList().size(); ++i) { if (!m_hlDict[schema].contains(i)) { - QList list; + QVector list; KateHlManager::self()->getHl(i)->getKateExtendedAttributeListCopy(schema, list); m_hlDict[schema].insert(i, list); } progress.setValue(progress.value() + 1); } progress.setValue(KateHlManager::self()->modeList().size()); return true; } void KateSchemaConfigHighlightTab::schemaChanged(const QString &schema) { m_schema = schema; m_styles->clear(); if (!m_hlDict.contains(m_schema)) { - m_hlDict.insert(schema, QHash >()); + m_hlDict.insert(schema, QHash >()); } if (!m_hlDict[m_schema].contains(m_hl)) { - QList list; + QVector list; KateHlManager::self()->getHl(m_hl)->getKateExtendedAttributeListCopy(m_schema, list); m_hlDict[m_schema].insert(m_hl, list); } KateAttributeList *l = m_defaults->attributeList(schema); // Set listview colors updateColorPalette(l->at(0)->foreground().color()); QHash prefixes; - QList::ConstIterator it = m_hlDict[m_schema][m_hl].constBegin(); + QVector::ConstIterator it = m_hlDict[m_schema][m_hl].constBegin(); while (it != m_hlDict[m_schema][m_hl].constEnd()) { const KTextEditor::Attribute::Ptr itemData = *it; Q_ASSERT(itemData); // All stylenames have their language mode prefixed, e.g. HTML:Comment // split them and put them into nice substructures. int c = itemData->name().indexOf(QLatin1Char(':')); if (c > 0) { QString prefix = itemData->name().left(c); QString name = itemData->name().mid(c + 1); QTreeWidgetItem *parent = prefixes[prefix]; if (! parent) { parent = new QTreeWidgetItem(m_styles, QStringList() << prefix); m_styles->expandItem(parent); prefixes.insert(prefix, parent); } m_styles->addItem(parent, name, l->at(itemData->defaultStyle()), itemData); } else { m_styles->addItem(itemData->name(), l->at(itemData->defaultStyle()), itemData); } ++it; } m_styles->resizeColumns(); } void KateSchemaConfigHighlightTab::updateColorPalette(const QColor &textColor) { QPalette p(m_styles->palette()); p.setColor(QPalette::Base, m_colorTab->backgroundColor()); p.setColor(QPalette::Highlight, m_colorTab->selectionColor()); p.setColor(QPalette::Text, textColor); m_styles->setPalette(p); } void KateSchemaConfigHighlightTab::reload() { m_styles->clear(); m_hlDict.clear(); hlChanged(hlCombo->currentIndex()); } void KateSchemaConfigHighlightTab::apply() { - QMutableHashIterator > > it = m_hlDict; + QMutableHashIterator > > it = m_hlDict; while (it.hasNext()) { it.next(); - QMutableHashIterator > it2 = it.value(); + QMutableHashIterator > it2 = it.value(); while (it2.hasNext()) { it2.next(); KateHlManager::self()->getHl(it2.key())->setKateExtendedAttributeList(it.key(), it2.value()); } } } QList KateSchemaConfigHighlightTab::hlsForSchema(const QString &schema) { return m_hlDict[schema].keys(); } void KateSchemaConfigHighlightTab::importHl(const QString &fromSchemaName, QString schema, int hl, KConfig *cfg) { QString schemaNameForLoading(fromSchemaName); QString hlName; bool doManage = (cfg == nullptr); if (schema.isEmpty()) { schema = m_schema; } if (doManage) { QString srcName = QFileDialog::getOpenFileName(this, i18n("Importing colors for single highlighting"), KateHlManager::self()->getHl(hl)->name() + QLatin1String(".katehlcolor"), QStringLiteral("%1 (*.katehlcolor)").arg(i18n("Kate color schema"))); if (srcName.isEmpty()) { return; } cfg = new KConfig(srcName, KConfig::SimpleConfig); KConfigGroup grp(cfg, "KateHLColors"); hlName = grp.readEntry("highlight", QString()); schemaNameForLoading = grp.readEntry("schema", QString()); if ((grp.readEntry("full schema", "true").toUpper() != QLatin1String("FALSE")) || hlName.isEmpty() || schemaNameForLoading.isEmpty()) { //ERROR - file format KMessageBox::information( this, i18n("File is not a single highlighting color file"), i18n("Fileformat error")); hl = -1; schemaNameForLoading = QString(); } else { hl = KateHlManager::self()->nameFind(hlName); if (hl == -1) { //hl not found KMessageBox::information( this, i18n("The selected file contains colors for a non existing highlighting:%1", hlName), i18n("Import failure")); hl = -1; schemaNameForLoading = QString(); } } } if ((hl != -1) && (!schemaNameForLoading.isEmpty())) { - QList list; + QVector list; KateHlManager::self()->getHl(hl)->getKateExtendedAttributeListCopy(schemaNameForLoading, list, cfg); KateHlManager::self()->getHl(hl)->setKateExtendedAttributeList(schema, list); m_hlDict[schema].insert(hl, list); } if (cfg && doManage) { apply(); delete cfg; cfg = nullptr; if ((hl != -1) && (!schemaNameForLoading.isEmpty())) { hlChanged(m_hl); KMessageBox::information( this, i18n("Colors have been imported for highlighting: %1", hlName), i18n("Import has finished")); } } } void KateSchemaConfigHighlightTab::exportHl(QString schema, int hl, KConfig *cfg) { bool doManage = (cfg == nullptr); if (schema.isEmpty()) { schema = m_schema; } if (hl == -1) { hl = m_hl; } - QList items = m_hlDict[schema][hl]; + QVector items = m_hlDict[schema][hl]; if (doManage) { QString destName = QFileDialog::getSaveFileName(this, i18n("Exporting colors for single highlighting: %1", KateHlManager::self()->getHl(hl)->name()), KateHlManager::self()->getHl(hl)->name() + QLatin1String(".katehlcolor"), QStringLiteral("%1 (*.katehlcolor)").arg(i18n("Kate color schema"))); if (destName.isEmpty()) { return; } cfg = new KConfig(destName, KConfig::SimpleConfig); KConfigGroup grp(cfg, "KateHLColors"); grp.writeEntry("highlight", KateHlManager::self()->getHl(hl)->name()); grp.writeEntry("schema", schema); grp.writeEntry("full schema", "false"); } KateHlManager::self()->getHl(hl)->setKateExtendedAttributeList(schema, items, cfg, doManage); if (doManage) { cfg->sync(); delete cfg; } } void KateSchemaConfigHighlightTab::showEvent(QShowEvent *event) { if (!event->spontaneous()) { KateAttributeList *l = m_defaults->attributeList(m_schema); Q_ASSERT(l != nullptr); updateColorPalette(l->at(0)->foreground().color()); } QWidget::showEvent(event); } //END KateSchemaConfigHighlightTab //BEGIN KateSchemaConfigPage -- Main dialog page KateSchemaConfigPage::KateSchemaConfigPage(QWidget *parent) : KateConfigPage(parent), m_currentSchema(-1) { QVBoxLayout *layout = new QVBoxLayout(this); layout->setMargin(0); // header QHBoxLayout *headerLayout = new QHBoxLayout; layout->addLayout(headerLayout); QLabel *lHl = new QLabel(i18n("&Schema:"), this); headerLayout->addWidget(lHl); schemaCombo = new KComboBox(this); schemaCombo->setEditable(false); lHl->setBuddy(schemaCombo); headerLayout->addWidget(schemaCombo); connect(schemaCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(comboBoxIndexChanged(int))); QPushButton *btnnew = new QPushButton(i18n("&New..."), this); headerLayout->addWidget(btnnew); connect(btnnew, SIGNAL(clicked()), this, SLOT(newSchema())); btndel = new QPushButton(i18n("&Delete"), this); headerLayout->addWidget(btndel); connect(btndel, SIGNAL(clicked()), this, SLOT(deleteSchema())); QPushButton *btnexport = new QPushButton(i18n("Export..."), this); headerLayout->addWidget(btnexport); connect(btnexport, SIGNAL(clicked()), this, SLOT(exportFullSchema())); QPushButton *btnimport = new QPushButton(i18n("Import..."), this); headerLayout->addWidget(btnimport); connect(btnimport, SIGNAL(clicked()), this, SLOT(importFullSchema())); headerLayout->addStretch(); // tabs QTabWidget *tabWidget = new QTabWidget(this); layout->addWidget(tabWidget); m_colorTab = new KateSchemaConfigColorTab(); tabWidget->addTab(m_colorTab, i18n("Colors")); connect(m_colorTab, SIGNAL(changed()), SLOT(slotChanged())); m_fontTab = new KateSchemaConfigFontTab(); tabWidget->addTab(m_fontTab, i18n("Font")); connect(m_fontTab, SIGNAL(changed()), SLOT(slotChanged())); m_defaultStylesTab = new KateSchemaConfigDefaultStylesTab(m_colorTab); tabWidget->addTab(m_defaultStylesTab, i18n("Default Text Styles")); connect(m_defaultStylesTab, SIGNAL(changed()), SLOT(slotChanged())); m_highlightTab = new KateSchemaConfigHighlightTab(m_defaultStylesTab, m_colorTab); tabWidget->addTab(m_highlightTab, i18n("Highlighting Text Styles")); connect(m_highlightTab, SIGNAL(changed()), SLOT(slotChanged())); QHBoxLayout *footLayout = new QHBoxLayout; layout->addLayout(footLayout); lHl = new QLabel(i18n("&Default schema for %1:", QCoreApplication::applicationName()), this); footLayout->addWidget(lHl); defaultSchemaCombo = new KComboBox(this); footLayout->addWidget(defaultSchemaCombo); defaultSchemaCombo->setEditable(false); lHl->setBuddy(defaultSchemaCombo); reload(); connect(defaultSchemaCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(slotChanged())); } KateSchemaConfigPage::~KateSchemaConfigPage() { } void KateSchemaConfigPage::exportFullSchema() { // get save destination const QString currentSchemaName = m_currentSchema; QString destName = QFileDialog::getSaveFileName(this, i18n("Exporting color schema: %1", currentSchemaName), currentSchemaName + QLatin1String(".kateschema"), QStringLiteral("%1 (*.kateschema)").arg(i18n("Kate color schema"))); if (destName.isEmpty()) { return; } // open config file KConfig cfg(destName, KConfig::SimpleConfig); // // export editor Colors (background, ...) // KConfigGroup colorConfigGroup(&cfg, "Editor Colors"); m_colorTab->exportSchema(colorConfigGroup); // // export Default Styles // m_defaultStylesTab->exportSchema(m_currentSchema, &cfg); // // export Highlighting Text Styles // // force a load of all Highlighting Text Styles QStringList hlList; m_highlightTab->loadAllHlsForSchema(m_currentSchema); QList hls = m_highlightTab->hlsForSchema(m_currentSchema); int cnt = 0; QProgressDialog progress(i18n("Exporting schema"), QString(), 0, hls.count(), this); progress.setWindowModality(Qt::WindowModal); foreach (int hl, hls) { hlList << KateHlManager::self()->getHl(hl)->name(); m_highlightTab->exportHl(m_currentSchema, hl, &cfg); progress.setValue(++cnt); if (progress.wasCanceled()) { break; } } progress.setValue(hls.count()); KConfigGroup grp(&cfg, "KateSchema"); grp.writeEntry("full schema", "true"); grp.writeEntry("highlightings", hlList); grp.writeEntry("schema", currentSchemaName); m_fontTab->exportSchema(grp); cfg.sync(); } QString KateSchemaConfigPage::requestSchemaName(const QString &suggestedName) { QString schemaName = suggestedName; bool reask = true; do { QDialog howToImportDialog(this); Ui_KateHowToImportSchema howToImport; QVBoxLayout *mainLayout = new QVBoxLayout; howToImportDialog.setLayout(mainLayout); QWidget *w = new QWidget(&howToImportDialog); mainLayout->addWidget(w); howToImport.setupUi(w); QDialogButtonBox *buttons = new QDialogButtonBox(&howToImportDialog); mainLayout->addWidget(buttons); QPushButton *okButton = new QPushButton; okButton->setDefault(true); KGuiItem::assign(okButton, KStandardGuiItem::ok()); buttons->addButton(okButton, QDialogButtonBox::AcceptRole); connect(okButton, SIGNAL(clicked()), &howToImportDialog, SLOT(accept())); QPushButton *cancelButton = new QPushButton; KGuiItem::assign(cancelButton, KStandardGuiItem::cancel()); buttons->addButton(cancelButton, QDialogButtonBox::RejectRole); connect(cancelButton, SIGNAL(clicked()), &howToImportDialog, SLOT(reject())); // // if schema exists, prepare option to replace if (KTextEditor::EditorPrivate::self()->schemaManager()->schema(schemaName).exists()) { howToImport.radioReplaceExisting->show(); howToImport.radioReplaceExisting->setText(i18n("Replace existing schema %1", schemaName)); howToImport.radioReplaceExisting->setChecked(true); } else { howToImport.radioReplaceExisting->hide(); howToImport.newName->setText(schemaName); } // cancel pressed? if (howToImportDialog.exec() == QDialog::Rejected) { schemaName.clear(); reask = false; } // check what the user wants else { // replace existing if (howToImport.radioReplaceExisting->isChecked()) { reask = false; } // replace current else if (howToImport.radioReplaceCurrent->isChecked()) { schemaName = m_currentSchema; reask = false; } // new one, check again, whether the schema already exists else if (howToImport.radioAsNew->isChecked()) { schemaName = howToImport.newName->text(); if (KTextEditor::EditorPrivate::self()->schemaManager()->schema(schemaName).exists()) { reask = true; } else { reask = false; } } // should never happen else { reask = true; } } } while (reask); return schemaName; } void KateSchemaConfigPage::importFullSchema() { const QString srcName = QFileDialog::getOpenFileName(this, i18n("Importing Color Schema"), QString(), QStringLiteral("%1 (*.kateschema)").arg(i18n("Kate color schema"))); if (srcName.isEmpty()) { return; } // carete config + sanity check for full color schema KConfig cfg(srcName, KConfig::SimpleConfig); KConfigGroup schemaGroup(&cfg, "KateSchema"); if (schemaGroup.readEntry("full schema", "false").toUpper() != QLatin1String("TRUE")) { KMessageBox::sorry(this, i18n("The file does not contain a full color schema."), i18n("Fileformat error")); return; } // read color schema name const QStringList highlightings = schemaGroup.readEntry("highlightings", QStringList()); const QString fromSchemaName = schemaGroup.readEntry("schema", i18n("Name unspecified")); // request valid schema name const QString schemaName = requestSchemaName(fromSchemaName); if (schemaName.isEmpty()) { return; } // if the schema already exists, select it in the combo box if (schemaCombo->findData(schemaName) != -1) { schemaCombo->setCurrentIndex(schemaCombo->findData(schemaName)); } else { // it is really a new schema, easy meat :-) newSchema(schemaName); } // make sure the correct schema is activated schemaChanged(schemaName); // Finally, the correct schema is activated. // Next, start importing. // // import editor Colors (background, ...) // KConfigGroup colorConfigGroup(&cfg, "Editor Colors"); m_colorTab->importSchema(colorConfigGroup); // // import font // m_fontTab->importSchema(schemaGroup); // // import Default Styles // m_defaultStylesTab->importSchema(fromSchemaName, schemaName, &cfg); // // import all Highlighting Text Styles // // create mapping from highlighting name to internal id const int hlCount = KateHlManager::self()->modeList().size(); QHash nameToId; for (int i = 0; i < hlCount; ++i) { nameToId.insert(KateHlManager::self()->modeList().at(i).name(), i); } // may take some time, as we have > 200 highlightings int cnt = 0; QProgressDialog progress(i18n("Importing schema"), QString(), 0, highlightings.count(), this); progress.setWindowModality(Qt::WindowModal); foreach (const QString &hl, highlightings) { if (nameToId.contains(hl)) { const int i = nameToId[hl]; m_highlightTab->importHl(fromSchemaName, schemaName, i, &cfg); } progress.setValue(++cnt); } progress.setValue(highlightings.count()); } void KateSchemaConfigPage::apply() { // remember name + index const QString schemaName = schemaCombo->itemData(schemaCombo->currentIndex()).toString(); // first apply all tabs m_colorTab->apply(); m_fontTab->apply(); m_defaultStylesTab->apply(); m_highlightTab->apply(); // just sync the config and reload KTextEditor::EditorPrivate::self()->schemaManager()->config().sync(); KTextEditor::EditorPrivate::self()->schemaManager()->config().reparseConfiguration(); // clear all attributes for (int i = 0; i < KateHlManager::self()->modeList().size(); ++i) { KateHlManager::self()->getHl(i)->clearAttributeArrays(); } // than reload the whole stuff KateRendererConfig::global()->setSchema(defaultSchemaCombo->itemData(defaultSchemaCombo->currentIndex()).toString()); KateRendererConfig::global()->reloadSchema(); // sync the hl config for real KateHlManager::self()->getKConfig()->sync(); // KateSchemaManager::update() sorts the schema alphabetically, hence the // schema indexes change. Thus, repopulate the schema list... refillCombos(schemaCombo->itemData(schemaCombo->currentIndex()).toString(), defaultSchemaCombo->itemData(defaultSchemaCombo->currentIndex()).toString()); schemaChanged(schemaName); } void KateSchemaConfigPage::reload() { // now reload the config from disc KTextEditor::EditorPrivate::self()->schemaManager()->config().reparseConfiguration(); // reinitialize combo boxes refillCombos(KateRendererConfig::global()->schema(), KateRendererConfig::global()->schema()); // finally, activate the current schema again schemaChanged(schemaCombo->itemData(schemaCombo->currentIndex()).toString()); // all tabs need to reload to discard all the cached data, as the index // mapping may have changed m_colorTab->reload(); m_fontTab->reload(); m_defaultStylesTab->reload(); m_highlightTab->reload(); } void KateSchemaConfigPage::refillCombos(const QString &schemaName, const QString &defaultSchemaName) { schemaCombo->blockSignals(true); defaultSchemaCombo->blockSignals(true); // reinitialize combo boxes schemaCombo->clear(); defaultSchemaCombo->clear(); QList schemaList = KTextEditor::EditorPrivate::self()->schemaManager()->list(); foreach (const KateSchema &s, schemaList) { schemaCombo->addItem(s.translatedName(), s.rawName); defaultSchemaCombo->addItem(s.translatedName(), s.rawName); } // set the correct indexes again, fallback to always existing "Normal" int schemaIndex = schemaCombo->findData(schemaName); if (schemaIndex == -1) { schemaIndex = schemaCombo->findData(QLatin1String("Normal")); } int defaultSchemaIndex = defaultSchemaCombo->findData(defaultSchemaName); if (defaultSchemaIndex == -1) { defaultSchemaIndex = defaultSchemaCombo->findData(QLatin1String("Normal")); } Q_ASSERT(schemaIndex != -1); Q_ASSERT(defaultSchemaIndex != -1); defaultSchemaCombo->setCurrentIndex(defaultSchemaIndex); schemaCombo->setCurrentIndex(schemaIndex); schemaCombo->blockSignals(false); defaultSchemaCombo->blockSignals(false); } void KateSchemaConfigPage::reset() { reload(); } void KateSchemaConfigPage::defaults() { reload(); } void KateSchemaConfigPage::deleteSchema() { const int comboIndex = schemaCombo->currentIndex(); const QString schemaNameToDelete = schemaCombo->itemData(comboIndex).toString(); if (KTextEditor::EditorPrivate::self()->schemaManager()->schemaData(schemaNameToDelete).shippedDefaultSchema) { // Default and Printing schema cannot be deleted. return; } // kill group KTextEditor::EditorPrivate::self()->schemaManager()->config().deleteGroup(schemaNameToDelete); // fallback to Default schema schemaCombo->setCurrentIndex(schemaCombo->findData(QVariant(QStringLiteral("Normal")))); if (defaultSchemaCombo->currentIndex() == defaultSchemaCombo->findData(schemaNameToDelete)) { defaultSchemaCombo->setCurrentIndex(defaultSchemaCombo->findData(QVariant(QStringLiteral("Normal")))); } // remove schema from combo box schemaCombo->removeItem(comboIndex); defaultSchemaCombo->removeItem(comboIndex); // Reload the color tab, since it uses cached schemas m_colorTab->reload(); } bool KateSchemaConfigPage::newSchema(const QString &newName) { // get sane name QString schemaName(newName); if (newName.isEmpty()) { bool ok = false; schemaName = QInputDialog::getText(this, i18n("Name for New Schema"), i18n("Name:"), QLineEdit::Normal, i18n("New Schema"), &ok); if (!ok) { return false; } } // try if schema already around if (KTextEditor::EditorPrivate::self()->schemaManager()->schema(schemaName).exists()) { KMessageBox::information(this, i18n("

The schema %1 already exists.

Please choose a different schema name.

", schemaName), i18n("New Schema")); return false; } // append items to combo boxes schemaCombo->addItem(schemaName, QVariant(schemaName)); defaultSchemaCombo->addItem(schemaName, QVariant(schemaName)); // finally, activate new schema (last item in the list) schemaCombo->setCurrentIndex(schemaCombo->count() - 1); return true; } void KateSchemaConfigPage::schemaChanged(const QString &schema) { btndel->setEnabled(!KTextEditor::EditorPrivate::self()->schemaManager()->schemaData(schema).shippedDefaultSchema); // propagate changed schema to all tabs m_colorTab->schemaChanged(schema); m_fontTab->schemaChanged(schema); m_defaultStylesTab->schemaChanged(schema); m_highlightTab->schemaChanged(schema); // save current schema index m_currentSchema = schema; } void KateSchemaConfigPage::comboBoxIndexChanged(int currentIndex) { schemaChanged(schemaCombo->itemData(currentIndex).toString()); } QString KateSchemaConfigPage::name() const { return i18n("Fonts & Colors"); } QString KateSchemaConfigPage::fullName() const { return i18n("Font & Color Schemas"); } QIcon KateSchemaConfigPage::icon() const { return QIcon::fromTheme(QStringLiteral("preferences-desktop-color")); } //END KateSchemaConfigPage diff --git a/src/schema/kateschemaconfig.h b/src/schema/kateschemaconfig.h index c3f987e5..9e91e29f 100644 --- a/src/schema/kateschemaconfig.h +++ b/src/schema/kateschemaconfig.h @@ -1,216 +1,216 @@ /* This file is part of the KDE libraries Copyright (C) 2001-2003 Christoph Cullmann Copyright (C) 2002, 2003 Anders Lund Copyright (C) 2012 Dominik Haumann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef __KATE_SCHEMA_CONFIG_H__ #define __KATE_SCHEMA_CONFIG_H__ #include "katedialogs.h" #include "katecolortreewidget.h" #include "kateextendedattribute.h" #include #include class KateStyleTreeWidget; class KComboBox; class KateSchemaConfigColorTab : public QWidget { Q_OBJECT public: KateSchemaConfigColorTab(); ~KateSchemaConfigColorTab(); QColor backgroundColor() const; QColor selectionColor() const; public Q_SLOTS: void apply(); void reload(); void schemaChanged(const QString &newSchema); void importSchema(KConfigGroup &config); void exportSchema(KConfigGroup &config); Q_SIGNALS: void changed(); private: QVector colorItemList() const; QVector readConfig(KConfigGroup &config); private: // multiple shemas may be edited. Hence, we need one ColorList for each schema QMap > m_schemas; QString m_currentSchema; KateColorTreeWidget *ui; }; class KateSchemaConfigFontTab : public QWidget { Q_OBJECT public: KateSchemaConfigFontTab(); ~KateSchemaConfigFontTab(); public: void readConfig(KConfig *config); void importSchema(KConfigGroup &config); void exportSchema(KConfigGroup &config); public Q_SLOTS: void apply(); void reload(); void schemaChanged(const QString &newSchema); Q_SIGNALS: void changed(); private: class KFontChooser *m_fontchooser; QMap m_fonts; QString m_currentSchema; private Q_SLOTS: void slotFontSelected(const QFont &font); }; class KateSchemaConfigDefaultStylesTab : public QWidget { Q_OBJECT public: KateSchemaConfigDefaultStylesTab(KateSchemaConfigColorTab *colorTab); ~KateSchemaConfigDefaultStylesTab() override; Q_SIGNALS: void changed(); public: void schemaChanged(const QString &schema); void reload(); void apply(); KateAttributeList *attributeList(const QString &schema); void exportSchema(const QString &schema, KConfig *cfg); void importSchema(const QString &schemaName, const QString &schema, KConfig *cfg); protected: void showEvent(QShowEvent *event) override; void updateColorPalette(const QColor &textColor); private: KateStyleTreeWidget *m_defaultStyles; QHash m_defaultStyleLists; KateSchemaConfigColorTab *m_colorTab; QString m_currentSchema; }; class KateSchemaConfigHighlightTab : public QWidget { Q_OBJECT public: explicit KateSchemaConfigHighlightTab(KateSchemaConfigDefaultStylesTab *page, KateSchemaConfigColorTab *colorTab); ~KateSchemaConfigHighlightTab() override; void schemaChanged(const QString &schema); void reload(); void apply(); Q_SIGNALS: void changed(); protected Q_SLOTS: void hlChanged(int z); public Q_SLOTS: void exportHl(QString schema = QString(), int hl = -1, KConfig *cfg = nullptr); void importHl(const QString &fromSchemaName = QString(), QString schema = QString(), int hl = -1, KConfig *cfg = nullptr); protected: void showEvent(QShowEvent *event) override; void updateColorPalette(const QColor &textColor); private: KateSchemaConfigDefaultStylesTab *m_defaults; KateSchemaConfigColorTab *m_colorTab; KComboBox *hlCombo; KateStyleTreeWidget *m_styles; QString m_schema; int m_hl; - QHash > > m_hlDict; + QHash > > m_hlDict; public: QList hlsForSchema(const QString &schema); bool loadAllHlsForSchema(const QString &schema); }; class KateSchemaConfigPage : public KateConfigPage { Q_OBJECT public: explicit KateSchemaConfigPage(QWidget *parent); ~KateSchemaConfigPage() override; QString name() const override; QString fullName() const override; QIcon icon() const override; public Q_SLOTS: void apply() override; void reload() override; void reset() override; void defaults() override; void exportFullSchema(); void importFullSchema(); private Q_SLOTS: void deleteSchema(); bool newSchema(const QString &newName = QString()); void schemaChanged(const QString &schema); void comboBoxIndexChanged(int currentIndex); private: void refillCombos(const QString &schemaName, const QString &defaultSchemaName); QString requestSchemaName(const QString &suggestedName); private: QString m_currentSchema; class QPushButton *btndel; class KComboBox *defaultSchemaCombo; class KComboBox *schemaCombo; KateSchemaConfigColorTab *m_colorTab; KateSchemaConfigFontTab *m_fontTab; KateSchemaConfigDefaultStylesTab *m_defaultStylesTab; KateSchemaConfigHighlightTab *m_highlightTab; }; #endif diff --git a/src/syntax/kateextendedattribute.h b/src/syntax/kateextendedattribute.h index f86741bf..65e763be 100644 --- a/src/syntax/kateextendedattribute.h +++ b/src/syntax/kateextendedattribute.h @@ -1,53 +1,53 @@ /* This file is part of the KDE libraries Copyright (C) 2001,2002 Joseph Wenninger Copyright (C) 2001 Christoph Cullmann Copyright (C) 1999 Jochen Wilhelmy * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KATEEXTENDEDATTRIBUTE_H #define KATEEXTENDEDATTRIBUTE_H #include /** * Custom property types, which may or may not be supported by implementations. * Internally used */ enum CustomProperties { /// Draws an outline around the text Outline = QTextFormat::UserProperty, /// Changes the brush used to paint the text when it is selected SelectedForeground, /// Changes the brush used to paint the background when it is selected SelectedBackground, /// Determines whether background color is drawn over whitespace. Defaults to true. BackgroundFillWhitespace, /// Defined to allow storage of dynamic effect information AttributeDynamicEffect = 0x10A00, /// Defined for internal usage of KTextEditor implementations AttributeInternalProperty = 0x10E00, AttributeName = AttributeInternalProperty, AttributeDefaultStyleIndex, Spellchecking, /// Defined to allow 3rd party code to create their own custom attributes - you may use values at or above this property. AttributeUserProperty = 0x110000 }; -typedef QList KateAttributeList; +typedef QVector KateAttributeList; #endif diff --git a/src/syntax/katehighlight.cpp b/src/syntax/katehighlight.cpp index c9a51c9f..4e34a5e6 100644 --- a/src/syntax/katehighlight.cpp +++ b/src/syntax/katehighlight.cpp @@ -1,665 +1,665 @@ /* This file is part of the KDE libraries Copyright (C) 2007 Mirko Stocker Copyright (C) 2007 Matthew Woehlke Copyright (C) 2003, 2004 Anders Lund Copyright (C) 2003 Hamish Rodda Copyright (C) 2001,2002 Joseph Wenninger Copyright (C) 2001 Christoph Cullmann Copyright (C) 1999 Jochen Wilhelmy This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //BEGIN INCLUDES #include "katehighlight.h" #include "katetextline.h" #include "katedocument.h" #include "katerenderer.h" #include "kateglobal.h" #include "kateschema.h" #include "kateconfig.h" #include "kateextendedattribute.h" #include "katepartdebug.h" #include "katedefaultcolors.h" #include #include #include #include #include #include #include #include #include //END //BEGIN STATICS namespace { /** * convert from KSyntaxHighlighting => KTextEditor type * special handle non-1:1 things */ inline KTextEditor::DefaultStyle textStyleToDefaultStyle(const KSyntaxHighlighting::Theme::TextStyle textStyle) { // handle deviations if (textStyle == KSyntaxHighlighting::Theme::Error) { return KTextEditor::dsError; } if (textStyle == KSyntaxHighlighting::Theme::Others) { return KTextEditor::dsOthers; } // else: simple cast return static_cast(textStyle); } } //END //BEGIN KateHighlighting KateHighlighting::KateHighlighting(const KSyntaxHighlighting::Definition &def) { /** * get name and section, always works */ iName = def.name(); iSection = def.translatedSection(); /** * handle the "no highlighting" case */ if (!def.isValid()) { // dummy properties + formats m_properties.resize(1); m_propertiesForFormat.push_back(&m_properties[0]); m_formats.resize(1); m_formatsIdToIndex.insert(std::make_pair(m_formats[0].id(), 0)); // be done, all below is just for the real highlighting variants return; } /** * handle the real highlighting case */ noHl = false; iHidden = def.isHidden(); identifier = def.filePath(); iStyle = def.style(); m_indentation = def.indenter(); folding = def.foldingEnabled(); m_foldingIndentationSensitive = def.indentationBasedFoldingEnabled(); /** * tell the AbstractHighlighter the definition it shall use */ setDefinition(def); /** * get all included definitions, e.g. PHP for HTML highlighting */ auto definitions = definition().includedDefinitions(); /** * first: handle only really included definitions */ for (const auto &includedDefinition : definitions) embeddedHighlightingModes.push_back(includedDefinition.name()); /** * now: handle all, including this definition itself * create the format => attributes mapping * collect embedded highlightings, too * * we start with our definition as we want to have the default format * of the initial definition as attribute with index == 0 * * we collect additional properties in the m_properties and * map the formats to the right properties in m_propertiesForFormat */ definitions.push_front(definition()); m_properties.resize(definitions.size()); size_t propertiesIndex = 0; for (const auto & includedDefinition : definitions) { auto &properties = m_properties[propertiesIndex]; properties.definition = includedDefinition; for (const auto &emptyLine : includedDefinition.foldingIgnoreList()) properties.emptyLines.push_back(QRegularExpression(emptyLine)); properties.singleLineCommentMarker = includedDefinition.singleLineCommentMarker(); properties.singleLineCommentPosition = includedDefinition.singleLineCommentPosition(); const auto multiLineComment = includedDefinition.multiLineCommentMarker(); properties.multiLineCommentStart = multiLineComment.first; properties.multiLineCommentEnd = multiLineComment.second; // collect character characters for (const auto &enc : includedDefinition.characterEncodings()) { properties.characterEncodingsPrefixStore.addPrefix(enc.second); properties.characterEncodings[enc.second] = enc.first; properties.reverseCharacterEncodings[enc.first] = enc.second; } // collect formats for (const auto & format : includedDefinition.formats()) { // register format id => internal attributes, we want no clashs const auto nextId = m_formats.size(); m_formatsIdToIndex.insert(std::make_pair(format.id(), nextId)); m_formats.push_back(format); m_propertiesForFormat.push_back(&properties); } // advance to next properties ++propertiesIndex; } } void KateHighlighting::doHighlight(const Kate::TextLineData *prevLine, Kate::TextLineData *textLine, const Kate::TextLineData *nextLine, bool &ctxChanged, int tabWidth) { // default: no context change ctxChanged = false; // no text line => nothing to do if (!textLine) { return; } // in all cases, remove old hl, or we will grow to infinite ;) textLine->clearAttributesAndFoldings(); // reset folding start textLine->clearMarkedAsFoldingStart(); // no hl set, nothing to do more than the above cleaning ;) if (noHl) { return; } /** * ensure we arrive in clean state */ Q_ASSERT(!m_textLineToHighlight); Q_ASSERT(m_foldingStartToCount.isEmpty()); /** * highlight the given line via the abstract highlighter * a bit ugly: we set the line to highlight as member to be able to update its stats in the applyFormat and applyFolding member functions */ m_textLineToHighlight = textLine; const KSyntaxHighlighting::State initialState (!prevLine ? KSyntaxHighlighting::State() : prevLine->highlightingState()); const KSyntaxHighlighting::State endOfLineState = highlightLine(textLine->string(), initialState); m_textLineToHighlight = nullptr; /** * update highlighting state if needed */ if (textLine->highlightingState() != endOfLineState) { textLine->setHighlightingState(endOfLineState); ctxChanged = true; } /** * handle folding info computed and cleanup hash again, if there * check if folding is not balanced and we have more starts then ends * then this line is a possible folding start! */ if (!m_foldingStartToCount.isEmpty()) { /** * possible folding start, if imbalanced, aka hash not empty! */ textLine->markAsFoldingStartAttribute(); /** * clear hash for next doHighlight */ m_foldingStartToCount.clear(); } /** * check for indentation based folding */ if (m_foldingIndentationSensitive && (tabWidth > 0) && !textLine->markedAsFoldingStartAttribute()) { /** * compute if we increase indentation in next line */ if (endOfLineState.indentationBasedFoldingEnabled() && !isEmptyLine(textLine) && !isEmptyLine(nextLine) && (textLine->indentDepth(tabWidth) < nextLine->indentDepth(tabWidth))) { textLine->markAsFoldingStartIndentation(); } } } void KateHighlighting::applyFormat(int offset, int length, const KSyntaxHighlighting::Format &format) { // WE ATM assume ascending offset order Q_ASSERT(m_textLineToHighlight); Q_ASSERT(format.isValid()); // get internal attribute, must exist const auto it = m_formatsIdToIndex.find(format.id()); Q_ASSERT(it != m_formatsIdToIndex.end()); // remember highlighting info in our textline m_textLineToHighlight->addAttribute(Kate::TextLineData::Attribute(offset, length, it->second)); } void KateHighlighting::applyFolding(int offset, int, KSyntaxHighlighting::FoldingRegion region) { // WE ATM assume ascending offset order and don't care for length Q_ASSERT(m_textLineToHighlight); Q_ASSERT(region.isValid()); const int foldingValue = (region.type() == KSyntaxHighlighting::FoldingRegion::Begin) ? int(region.id()) : -int(region.id()); m_textLineToHighlight->addFolding(offset, foldingValue); /** * for each end region, decrement counter for that type, erase if count reaches 0! */ if (foldingValue < 0) { QHash::iterator end = m_foldingStartToCount.find(foldingValue); if (end != m_foldingStartToCount.end()) { if (end.value() > 1) { --(end.value()); } else { m_foldingStartToCount.erase(end); } } } /** * increment counter for each begin region! */ if (foldingValue > 0) { ++m_foldingStartToCount[foldingValue]; } } -void KateHighlighting::getKateExtendedAttributeList(const QString &schema, QList &list, KConfig *cfg) +void KateHighlighting::getKateExtendedAttributeList(const QString &schema, QVector &list, KConfig *cfg) { KConfigGroup config(cfg ? cfg : KateHlManager::self()->getKConfig(), QLatin1String("Highlighting ") + iName + QLatin1String(" - Schema ") + schema); list = attributesForDefinition(); foreach (KTextEditor::Attribute::Ptr p, list) { Q_ASSERT(p); QStringList s = config.readEntry(p->name(), QStringList()); // qCDebug(LOG_KTE)<name< 0) { while (s.count() < 10) { s << QString(); } QString name = p->name(); bool spellCheck = !p->skipSpellChecking(); p->clear(); p->setName(name); p->setSkipSpellChecking(!spellCheck); QString tmp = s[0]; if (!tmp.isEmpty()) { p->setDefaultStyle(static_cast (tmp.toInt())); } QRgb col; tmp = s[1]; if (!tmp.isEmpty()) { col = tmp.toUInt(nullptr, 16); p->setForeground(QColor(col)); } tmp = s[2]; if (!tmp.isEmpty()) { col = tmp.toUInt(nullptr, 16); p->setSelectedForeground(QColor(col)); } tmp = s[3]; if (!tmp.isEmpty()) { p->setFontBold(tmp != QLatin1String("0")); } tmp = s[4]; if (!tmp.isEmpty()) { p->setFontItalic(tmp != QLatin1String("0")); } tmp = s[5]; if (!tmp.isEmpty()) { p->setFontStrikeOut(tmp != QLatin1String("0")); } tmp = s[6]; if (!tmp.isEmpty()) { p->setFontUnderline(tmp != QLatin1String("0")); } tmp = s[7]; if (!tmp.isEmpty()) { col = tmp.toUInt(nullptr, 16); p->setBackground(QColor(col)); } tmp = s[8]; if (!tmp.isEmpty()) { col = tmp.toUInt(nullptr, 16); p->setSelectedBackground(QColor(col)); } tmp = s[9]; if (!tmp.isEmpty() && tmp != QLatin1String("---")) { p->setFontFamily(tmp); } } } } -void KateHighlighting::getKateExtendedAttributeListCopy(const QString &schema, QList< KTextEditor::Attribute::Ptr > &list, KConfig *cfg) +void KateHighlighting::getKateExtendedAttributeListCopy(const QString &schema, QVector &list, KConfig *cfg) { - QList attributes; + QVector attributes; getKateExtendedAttributeList(schema, attributes, cfg); list.clear(); foreach (const KTextEditor::Attribute::Ptr &attribute, attributes) { list.append(KTextEditor::Attribute::Ptr(new KTextEditor::Attribute(*attribute.data()))); } } -void KateHighlighting::setKateExtendedAttributeList(const QString &schema, QList &list, KConfig *cfg, bool writeDefaultsToo) +void KateHighlighting::setKateExtendedAttributeList(const QString &schema, QVector &list, KConfig *cfg, bool writeDefaultsToo) { KConfigGroup config(cfg ? cfg : KateHlManager::self()->getKConfig(), QLatin1String("Highlighting ") + iName + QLatin1String(" - Schema ") + schema); QStringList settings; KateAttributeList defList; KateHlManager::self()->getDefaults(schema, defList); foreach (const KTextEditor::Attribute::Ptr &p, list) { Q_ASSERT(p); settings.clear(); KTextEditor::DefaultStyle defStyle = p->defaultStyle(); KTextEditor::Attribute::Ptr a(defList[defStyle]); settings << QString::number(p->defaultStyle(), 10); settings << (p->hasProperty(QTextFormat::ForegroundBrush) ? QString::number(p->foreground().color().rgb(), 16) : (writeDefaultsToo ? QString::number(a->foreground().color().rgb(), 16) : QString())); settings << (p->hasProperty(SelectedForeground) ? QString::number(p->selectedForeground().color().rgb(), 16) : (writeDefaultsToo ? QString::number(a->selectedForeground().color().rgb(), 16) : QString())); settings << (p->hasProperty(QTextFormat::FontWeight) ? (p->fontBold() ? QStringLiteral("1") : QStringLiteral("0")) : (writeDefaultsToo ? (a->fontBold() ? QStringLiteral("1") : QStringLiteral("0")) : QString())); settings << (p->hasProperty(QTextFormat::FontItalic) ? (p->fontItalic() ? QStringLiteral("1") : QStringLiteral("0")) : (writeDefaultsToo ? (a->fontItalic() ? QStringLiteral("1") : QStringLiteral("0")) : QString())); settings << (p->hasProperty(QTextFormat::FontStrikeOut) ? (p->fontStrikeOut() ? QStringLiteral("1") : QStringLiteral("0")) : (writeDefaultsToo ? (a->fontStrikeOut() ? QStringLiteral("1") : QStringLiteral("0")) : QString())); settings << (p->hasProperty(QTextFormat::FontUnderline) ? (p->fontUnderline() ? QStringLiteral("1") : QStringLiteral("0")) : (writeDefaultsToo ? (a->fontUnderline() ? QStringLiteral("1") : QStringLiteral("0")) : QString())); settings << (p->hasProperty(QTextFormat::BackgroundBrush) ? QString::number(p->background().color().rgb(), 16) : ((writeDefaultsToo && a->hasProperty(QTextFormat::BackgroundBrush)) ? QString::number(a->background().color().rgb(), 16) : QString())); settings << (p->hasProperty(SelectedBackground) ? QString::number(p->selectedBackground().color().rgb(), 16) : ((writeDefaultsToo && a->hasProperty(SelectedBackground)) ? QString::number(a->selectedBackground().color().rgb(), 16) : QString())); settings << (p->hasProperty(QTextFormat::FontFamily) ? (p->fontFamily()) : (writeDefaultsToo ? a->fontFamily() : QString())); settings << QStringLiteral("---"); config.writeEntry(p->name(), settings); } } int KateHighlighting::sanitizeFormatIndex(int attrib) const { // sanitize, e.g. one could have old hl info with now invalid attribs if (attrib < 0 || size_t(attrib) >= m_formats.size()) { return 0; } return attrib; } const QHash &KateHighlighting::getCharacterEncodings(int attrib) const { return m_propertiesForFormat.at(sanitizeFormatIndex(attrib))->characterEncodings; } const KatePrefixStore &KateHighlighting::getCharacterEncodingsPrefixStore(int attrib) const { return m_propertiesForFormat.at(sanitizeFormatIndex(attrib))->characterEncodingsPrefixStore; } const QHash &KateHighlighting::getReverseCharacterEncodings(int attrib) const { return m_propertiesForFormat.at(sanitizeFormatIndex(attrib))->reverseCharacterEncodings; } bool KateHighlighting::attributeRequiresSpellchecking(int attr) { return m_formats[sanitizeFormatIndex(attr)].spellCheck(); } KTextEditor::DefaultStyle KateHighlighting::defaultStyleForAttribute(int attr) const { return textStyleToDefaultStyle(m_formats[sanitizeFormatIndex(attr)].textStyle()); } QString KateHighlighting::nameForAttrib(int attrib) const { const auto &format = m_formats.at(sanitizeFormatIndex(attrib)); return m_propertiesForFormat.at(sanitizeFormatIndex(attrib))->definition.name() + QLatin1Char(':') + QString(format.isValid() ? format.name() : QLatin1String("Normal")); } bool KateHighlighting::isInWord(QChar c, int attrib) const { return !m_propertiesForFormat.at(sanitizeFormatIndex(attrib))->definition.isWordDelimiter(c) && !c.isSpace() && c != QLatin1Char('"') && c != QLatin1Char('\'') && c != QLatin1Char('`'); } bool KateHighlighting::canBreakAt(QChar c, int attrib) const { return m_propertiesForFormat.at(sanitizeFormatIndex(attrib))->definition.isWordWrapDelimiter(c) && c != QLatin1Char('"') && c != QLatin1Char('\''); } const QVector &KateHighlighting::emptyLines(int attrib) const { return m_propertiesForFormat.at(sanitizeFormatIndex(attrib))->emptyLines; } bool KateHighlighting::canComment(int startAttrib, int endAttrib) const { const auto startProperties = m_propertiesForFormat.at(sanitizeFormatIndex(startAttrib)); const auto endProperties = m_propertiesForFormat.at(sanitizeFormatIndex(endAttrib)); return (startProperties == endProperties && ((!startProperties->multiLineCommentStart.isEmpty() && !startProperties->multiLineCommentEnd.isEmpty()) || !startProperties->singleLineCommentMarker.isEmpty())); } QString KateHighlighting::getCommentStart(int attrib) const { return m_propertiesForFormat.at(sanitizeFormatIndex(attrib))->multiLineCommentStart; } QString KateHighlighting::getCommentEnd(int attrib) const { return m_propertiesForFormat.at(sanitizeFormatIndex(attrib))->multiLineCommentEnd; } QString KateHighlighting::getCommentSingleLineStart(int attrib) const { return m_propertiesForFormat.at(sanitizeFormatIndex(attrib))->singleLineCommentMarker; } KSyntaxHighlighting::CommentPosition KateHighlighting::getCommentSingleLinePosition(int attrib) const { return m_propertiesForFormat.at(sanitizeFormatIndex(attrib))->singleLineCommentPosition; } const QHash &KateHighlighting::characterEncodings(int attrib) const { return m_propertiesForFormat.at(sanitizeFormatIndex(attrib))->characterEncodings; } void KateHighlighting::clearAttributeArrays() { // just clear the hashed attributes, we create them lazy again m_attributeArrays.clear(); } -QList KateHighlighting::attributesForDefinition() +QVector KateHighlighting::attributesForDefinition() { /** * create list of all known things */ - QList array; + QVector array; for (const auto &format : m_formats) { /** * FIXME: atm we just set some theme here for later color generation */ setTheme(KateHlManager::self()->repository().defaultTheme(KSyntaxHighlighting::Repository::LightTheme)); /** * create a KTextEditor attribute matching the given format */ KTextEditor::Attribute::Ptr newAttribute(new KTextEditor::Attribute(nameForAttrib(array.size()), textStyleToDefaultStyle(format.textStyle()))); if (format.hasTextColor(theme())) { newAttribute->setForeground(format.textColor(theme())); newAttribute->setSelectedForeground(format.selectedTextColor(theme())); } if (format.hasBackgroundColor(theme())) { newAttribute->setBackground(format.backgroundColor(theme())); newAttribute->setSelectedBackground(format.selectedBackgroundColor(theme())); } if (format.isBold(theme())) { newAttribute->setFontBold(true); } if (format.isItalic(theme())) { newAttribute->setFontItalic(true); } if (format.isUnderline(theme())) { newAttribute->setFontUnderline(true); } if (format.isStrikeThrough(theme())) { newAttribute->setFontStrikeOut(true); } newAttribute->setSkipSpellChecking(format.spellCheck()); array.append(newAttribute); } return array; } -QList KateHighlighting::attributes(const QString &schema) +QVector KateHighlighting::attributes(const QString &schema) { // found it, already floating around if (m_attributeArrays.contains(schema)) { return m_attributeArrays[schema]; } // k, schema correct, let create the data - QList array; + QVector array; KateAttributeList defaultStyleList; KateHlManager::self()->getDefaults(schema, defaultStyleList); - QList itemDataList; + QVector itemDataList; getKateExtendedAttributeList(schema, itemDataList); uint nAttribs = itemDataList.count(); for (uint z = 0; z < nAttribs; z++) { KTextEditor::Attribute::Ptr itemData = itemDataList.at(z); KTextEditor::Attribute::Ptr newAttribute(new KTextEditor::Attribute(*defaultStyleList.at(itemData->defaultStyle()))); if (itemData && itemData->hasAnyProperty()) { *newAttribute += *itemData; } array.append(newAttribute); } m_attributeArrays.insert(schema, array); return array; } QStringList KateHighlighting::getEmbeddedHighlightingModes() const { return embeddedHighlightingModes; } bool KateHighlighting::isEmptyLine(const Kate::TextLineData *textline) const { const QString &txt = textline->string(); if (txt.isEmpty()) { return true; } const auto &l = emptyLines(textline->attribute(0)); if (l.isEmpty()) { return false; } foreach (const QRegularExpression &re, l) { const QRegularExpressionMatch match = re.match (txt, 0, QRegularExpression::NormalMatch, QRegularExpression::AnchoredMatchOption); if (match.hasMatch() && match.capturedLength() == txt.length()) { return true; } } return false; } int KateHighlighting::attributeForLocation(KTextEditor::DocumentPrivate* doc, const KTextEditor::Cursor& cursor) { // Validate parameters to prevent out of range access if (cursor.line() < 0 || cursor.line() >= doc->lines() || cursor.column() < 0) { return 0; } // get highlighted line Kate::TextLine tl = doc->kateTextLine(cursor.line()); // make sure the textline is a valid pointer if (!tl) { return 0; } /** * either get char attribute or attribute of context still active at end of line */ if (cursor.column() < tl->length()) { return sanitizeFormatIndex(tl->attribute(cursor.column())); } else if (cursor.column() >= tl->length()) { if (!tl->attributesList().isEmpty()) { return sanitizeFormatIndex(tl->attributesList().back().attributeValue); } } return 0; } QStringList KateHighlighting::keywordsForLocation(KTextEditor::DocumentPrivate* doc, const KTextEditor::Cursor& cursor) { // FIXME-SYNTAX: was before more precise, aka context level const auto &def = m_propertiesForFormat.at(attributeForLocation(doc, cursor))->definition; QStringList keywords; for (const auto &keylist : def.keywordLists()) { keywords += def.keywordList(keylist); } return keywords; } bool KateHighlighting::spellCheckingRequiredForLocation(KTextEditor::DocumentPrivate* doc, const KTextEditor::Cursor& cursor) { return m_formats.at(attributeForLocation(doc, cursor)).spellCheck(); } QString KateHighlighting::higlightingModeForLocation(KTextEditor::DocumentPrivate* doc, const KTextEditor::Cursor& cursor) { return m_propertiesForFormat.at(attributeForLocation(doc, cursor))->definition.name(); } //END diff --git a/src/syntax/katehighlight.h b/src/syntax/katehighlight.h index 2ccc277b..8f0f9810 100644 --- a/src/syntax/katehighlight.h +++ b/src/syntax/katehighlight.h @@ -1,354 +1,354 @@ /* This file is part of the KDE libraries Copyright (C) 2001,2002 Joseph Wenninger Copyright (C) 2001 Christoph Cullmann Copyright (C) 1999 Jochen Wilhelmy * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef __KATE_HIGHLIGHT_H__ #define __KATE_HIGHLIGHT_H__ #include #include #include #include #include "katetextline.h" #include "kateextendedattribute.h" #include "katesyntaxmanager.h" #include "spellcheck/prefixstore.h" #include "range.h" #include #include #include #include #include #include #include #include #include #include class KConfig; namespace KTextEditor { class DocumentPrivate; } class KateHighlighting : private KSyntaxHighlighting::AbstractHighlighter { public: KateHighlighting(const KSyntaxHighlighting::Definition &def); protected: /** * Reimplement this to apply formats to your output. The provided @p format * is valid for the interval [@p offset, @p offset + @p length). * * @param offset The start column of the interval for which @p format matches * @param length The length of the matching text * @param format The Format that applies to the range [offset, offset + length) * * @note Make sure to set a valid Definition, otherwise the parameter * @p format is invalid for the entire line passed to highlightLine() * (cf. Format::isValid()). * * @see applyFolding(), highlightLine() */ virtual void applyFormat(int offset, int length, const KSyntaxHighlighting::Format &format) override; /** * Reimplement this to apply folding to your output. The provided * FoldingRegion @p region either stars or ends a code folding region in the * interval [@p offset, @p offset + @p length). * * @param offset The start column of the FoldingRegion * @param length The length of the matching text that starts / ends a * folding region * @param region The FoldingRegion that applies to the range [offset, offset + length) * * @note The FoldingRegion @p region is @e always either of type * FoldingRegion::Type::Begin or FoldingRegion::Type::End. * * @see applyFormat(), highlightLine(), FoldingRegion */ virtual void applyFolding(int offset, int length, KSyntaxHighlighting::FoldingRegion region) override; public: /** * Parse the text and fill in the context array and folding list array * * @param prevLine The previous line, the context array is picked up from that if present. * @param textLine The text line to parse * @param nextLine The next line, to check if indentation changed for indentation based folding. * @param ctxChanged will be set to reflect if the context changed * @param tabWidth tab width for indentation based folding, if wanted, else 0 */ void doHighlight(const Kate::TextLineData *prevLine, Kate::TextLineData *textLine, const Kate::TextLineData *nextLine, bool &ctxChanged, int tabWidth = 0); /** * Saves the attribute definitions to the config file. * * @param schema The id of the schema group to save * @param list QList containing the data to be used */ - void setKateExtendedAttributeList(const QString &schema, QList &list, + void setKateExtendedAttributeList(const QString &schema, QVector &list, KConfig *cfg = nullptr /*if 0 standard kate config*/, bool writeDefaultsToo = false); const QString &name() const { return iName; } const QString §ion() const { return iSection; } bool hidden() const { return iHidden; } const QString &style() const { return iStyle; } const QString &getIdentifier() const { return identifier; } /** * @return true if the character @p c is not a deliminator character * for the corresponding highlight. */ bool isInWord(QChar c, int attrib = 0) const; /** * @return true if the character @p c is a wordwrap deliminator as specified * in the general keyword section of the xml file. */ bool canBreakAt(QChar c, int attrib = 0) const; /** * */ const QVector &emptyLines(int attribute = 0) const; bool isEmptyLine(const Kate::TextLineData *textline) const; /** * @return true if @p beginAttr and @p endAttr are members of the same * highlight, and there are comment markers of either type in that. */ bool canComment(int startAttr, int endAttr) const; /** * @return the mulitiline comment start marker for the highlight * corresponding to @p attrib. */ QString getCommentStart(int attrib = 0) const; /** * @return the muiltiline comment end marker for the highlight corresponding * to @p attrib. */ QString getCommentEnd(int attrib = 0) const; /** * @return the single comment marker for the highlight corresponding * to @p attrib. */ QString getCommentSingleLineStart(int attrib = 0) const; const QHash &characterEncodings(int attrib = 0) const; /** * @return the single comment marker position for the highlight corresponding * to @p attrib. */ KSyntaxHighlighting::CommentPosition getCommentSingleLinePosition(int attrib = 0) const; bool attributeRequiresSpellchecking(int attr); /** * map attribute to its name * @return name of the attribute */ QString nameForAttrib(int attrib) const; /** * Get attribute for the given cursor position. * @param doc document to use * @param cursor cursor position in the given document * @return attribute valid at that location, default is 0 */ int attributeForLocation(KTextEditor::DocumentPrivate* doc, const KTextEditor::Cursor& cursor); /** * Get all keywords valid for the given cursor position. * @param doc document to use * @param cursor cursor position in the given document * @return all keywords valid at that location */ QStringList keywordsForLocation(KTextEditor::DocumentPrivate* doc, const KTextEditor::Cursor& cursor); /** * Is spellchecking required for the tiven cursor position? * @param doc document to use * @param cursor cursor position in the given document * @return spell checking required? */ bool spellCheckingRequiredForLocation(KTextEditor::DocumentPrivate* doc, const KTextEditor::Cursor& cursor); /** * Get highlighting mode for the given cursor position. * @param doc document to use * @param cursor cursor position in the given document * @return mode valid at that location */ QString higlightingModeForLocation(KTextEditor::DocumentPrivate* doc, const KTextEditor::Cursor& cursor); KTextEditor::DefaultStyle defaultStyleForAttribute(int attr) const; void clearAttributeArrays(); - QList attributes(const QString &schema); + QVector attributes(const QString &schema); inline bool noHighlighting() const { return noHl; } /** * Indentation mode, e.g. c-style, .... * @return indentation mode */ const QString &indentation() const { return m_indentation; } - void getKateExtendedAttributeList(const QString &schema, QList &, KConfig *cfg = nullptr); - void getKateExtendedAttributeListCopy(const QString &schema, QList &, KConfig *cfg = nullptr); + void getKateExtendedAttributeList(const QString &schema, QVector &, KConfig *cfg = nullptr); + void getKateExtendedAttributeListCopy(const QString &schema, QVector &, KConfig *cfg = nullptr); const QHash &getCharacterEncodings(int attrib) const; const KatePrefixStore &getCharacterEncodingsPrefixStore(int attrib) const; const QHash &getReverseCharacterEncodings(int attrib) const; /** * Returns a list of names of embedded modes. */ QStringList getEmbeddedHighlightingModes() const; private: /** * create list of attributes from internal formats with properties as defined in syntax file * @return attributes list with attributes as defined in syntax file */ - QList attributesForDefinition(); + QVector attributesForDefinition(); int sanitizeFormatIndex(int attrib) const; private: QStringList embeddedHighlightingModes; bool noHl = true; bool folding = false; QString iName; QString iSection; bool iHidden = false; QString identifier; QString iStyle; /** * Indentation mode, e.g. c-style, .... */ QString m_indentation; bool m_foldingIndentationSensitive = false; // map schema name to attributes... - QHash< QString, QList > m_attributeArrays; + QHash< QString, QVector > m_attributeArrays; /** * This class holds the additional properties for one highlight * definition, such as comment strings, deliminators etc. */ class HighlightPropertyBag { public: KSyntaxHighlighting::Definition definition; QString singleLineCommentMarker; QString multiLineCommentStart; QString multiLineCommentEnd; KSyntaxHighlighting::CommentPosition singleLineCommentPosition; QVector emptyLines; QHash characterEncodings; KatePrefixStore characterEncodingsPrefixStore; QHash reverseCharacterEncodings; }; public: inline bool foldingIndentationSensitive() { return m_foldingIndentationSensitive; } inline bool allowsFolding() { return folding; } /** * Highlight properties for this definition and each included highlight definition. */ std::vector m_properties; /** * all formats for the highlighting definition of this highlighting * includes included formats */ std::vector m_formats; /** * for each format, pointer to the matching HighlightPropertyBag in m_properties */ std::vector m_propertiesForFormat; /** * mapping of format id => index into m_formats */ std::unordered_map m_formatsIdToIndex; /** * textline to do updates on during doHighlight */ Kate::TextLineData *m_textLineToHighlight = nullptr; /** * check if the folding begin/ends are balanced! * updated during doHighlight */ QHash m_foldingStartToCount; }; #endif