File indexing completed on 2024-12-22 04:40:10
0001 /* 0002 SPDX-FileCopyrightText: 2022 Mladen Milinkovic <max@smoothware.net> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "csshighlighter.h" 0008 0009 #include <QDebug> 0010 #include <QGuiApplication> 0011 #include <QPalette> 0012 0013 using namespace SubtitleComposer; 0014 0015 union BlockState { 0016 int state; 0017 struct { 0018 quint16 indent; 0019 bool commentStart : 1; 0020 bool attributeStart : 1; 0021 bool styleStart : 1; 0022 bool stringStart : 1; 0023 bool valueStart : 1; 0024 }; 0025 }; 0026 0027 CSSHighlighter::CSSHighlighter(QTextDocument *parent) 0028 : QSyntaxHighlighter(parent) 0029 { 0030 onPaletteChanged(); 0031 } 0032 0033 static QColor 0034 correctedColor(const QColor &color) 0035 { 0036 int h, s, v; 0037 color.getHsv(&h, &s, &v); 0038 return QColor::fromHsv(h, s, qMin(v * 5 / 2, 255)); 0039 } 0040 0041 bool 0042 CSSHighlighter::event(QEvent *ev) 0043 { 0044 if(ev->type() == QEvent::ApplicationPaletteChange || ev->type() == QEvent::PaletteChange) { 0045 onPaletteChanged(); 0046 } 0047 0048 return QSyntaxHighlighter::event(ev); 0049 } 0050 0051 void 0052 CSSHighlighter::onPaletteChanged() 0053 { 0054 const QPalette pal = QGuiApplication::palette(); 0055 bool dark = pal.color(QPalette::Window).value() < pal.color(QPalette::WindowText).value(); 0056 m_commentFormat.setForeground(dark ? correctedColor(Qt::cyan) : Qt::cyan); 0057 m_attributeFormat.setForeground(dark ? correctedColor(Qt::green) : Qt::green); 0058 m_styleFormat.setForeground(dark ? correctedColor(Qt::yellow) : Qt::yellow); 0059 m_stringFormat.setForeground(dark ? correctedColor(Qt::red) : Qt::red); 0060 m_valueFormat.setForeground(dark ? correctedColor(Qt::white) : Qt::white); 0061 } 0062 0063 #define FMT_START(x) { x ## Start = i; } 0064 #define FMT_END(x, pos) { setFormat(x ## Start, pos - x ## Start, m_ ## x ## Format); x ## Start = -1; } 0065 0066 void 0067 CSSHighlighter::highlightBlock(const QString &text) 0068 { 0069 // init state 0070 BlockState state = { qMax(0, previousBlockState()) }; 0071 int commentStart = state.commentStart ? 0 : -1; 0072 int attributeStart = state.attributeStart ? 0 : -1; 0073 int styleStart = state.styleStart ? 0 : -1; 0074 int stringStart = state.stringStart ? 0 : -1; 0075 int valueStart = state.valueStart ? 0 : -1; 0076 quint16 indent = state.indent; 0077 0078 // process 0079 const int last = text.size() - 1; 0080 for(int i = 0; i <= last; i++) { 0081 const QChar &ch = text.at(i); 0082 if(commentStart != -1) { 0083 if(ch == QChar('*') && i != last && text.at(i + 1) == QChar('/')) { 0084 i++; 0085 FMT_END(comment, i + 1); 0086 } 0087 } else if(ch == QChar('/') && i != last && text.at(i + 1) == QChar('*')) { 0088 FMT_START(comment) 0089 i++; 0090 } else if(ch == QChar('{') && stringStart == -1) { 0091 indent++; 0092 if(attributeStart != -1) 0093 FMT_END(attribute, i); 0094 styleStart = -1; 0095 if(valueStart != -1) 0096 FMT_END(value, i); 0097 } else if(ch == QChar('}') && stringStart == -1) { 0098 indent--; 0099 styleStart = -1; 0100 if(valueStart != -1) 0101 FMT_END(value, i); 0102 } else if(indent == 0) { 0103 if(attributeStart == -1) { 0104 if(ch != QChar(',')) 0105 FMT_START(attribute); 0106 } else { 0107 if(ch.isSpace() || ch == QChar(',')) 0108 FMT_END(attribute, i); 0109 } 0110 } else { 0111 if(stringStart != -1) { 0112 if(ch == QChar('"')) { 0113 FMT_END(string, i + 1); 0114 if(valueStart != -1) 0115 valueStart = i != last ? i + 1 : -1; 0116 } 0117 } else if(valueStart != -1) { 0118 if(ch == QChar(';')) { 0119 FMT_END(value, i + 1); 0120 } else if(ch == QChar('"')) { 0121 FMT_START(string); 0122 } 0123 } else if(styleStart == -1) { 0124 if(!ch.isSpace() && ch != QChar(':')) 0125 FMT_START(style); 0126 } else { 0127 if(ch.isSpace() || ch == QChar(';')) { 0128 styleStart = -1; 0129 } else if(ch == QChar(':')) { 0130 FMT_END(style, i); 0131 while(i != last) { 0132 const QChar &ch = text.at(++i); 0133 if(ch.isSpace()) 0134 continue; 0135 FMT_START(value); 0136 if(ch == QChar('"')) 0137 FMT_START(string); 0138 break; 0139 } 0140 } 0141 } 0142 0143 } 0144 } 0145 0146 // cleanup 0147 if(commentStart != -1) 0148 FMT_END(comment, last + 1); 0149 if(attributeStart != -1) 0150 setFormat(attributeStart, last - attributeStart + 1, m_attributeFormat); 0151 if(styleStart != -1) 0152 setFormat(styleStart, last - styleStart + 1, m_styleFormat); 0153 if(stringStart != -1) 0154 setFormat(stringStart, last - stringStart + 1, m_stringFormat); 0155 else if(valueStart != -1) 0156 setFormat(valueStart, last - valueStart + 1, m_valueFormat); 0157 0158 // store state 0159 state.commentStart = commentStart != -1; 0160 state.attributeStart = attributeStart != -1; 0161 state.styleStart = styleStart != -1; 0162 state.stringStart = stringStart != -1; 0163 state.valueStart = valueStart != -1; 0164 state.indent = indent; 0165 setCurrentBlockState(state.state); 0166 }