1 //============================================================================= 2 // MuseScore 3 // Music Composition & Notation 4 // 5 // Copyright (C) 2014 Werner Schweer 6 // 7 // This program is free software; you can redistribute it and/or modify 8 // it under the terms of the GNU General Public License version 2 9 // as published by the Free Software Foundation and appearing in 10 // the file LICENCE.GPL 11 //============================================================================= 12 13 #ifndef __TEXTBASE_H__ 14 #define __TEXTBASE_H__ 15 16 #include "element.h" 17 #include "property.h" 18 #include "style.h" 19 20 namespace Ms { 21 22 class MuseScoreView; 23 struct SymCode; 24 class TextBase; 25 class TextBlock; 26 class ChangeText; 27 28 //--------------------------------------------------------- 29 // FrameType 30 //--------------------------------------------------------- 31 32 enum class FrameType : char { 33 NO_FRAME, SQUARE, CIRCLE 34 }; 35 36 //--------------------------------------------------------- 37 // VerticalAlignment 38 //--------------------------------------------------------- 39 40 enum class VerticalAlignment : char { 41 AlignNormal, AlignSuperScript, AlignSubScript 42 }; 43 44 //--------------------------------------------------------- 45 // FormatId 46 //--------------------------------------------------------- 47 48 enum class FormatId : char { 49 Bold, Italic, Underline, Valign, FontSize, FontFamily 50 }; 51 52 //--------------------------------------------------------- 53 // MultiClick 54 //--------------------------------------------------------- 55 56 enum class MultiClick : char { 57 Double, Triple 58 }; 59 60 //--------------------------------------------------------- 61 // CharFormat 62 //--------------------------------------------------------- 63 64 class CharFormat { 65 FontStyle _style { FontStyle::Normal }; 66 bool _preedit { false }; 67 VerticalAlignment _valign { VerticalAlignment::AlignNormal }; 68 qreal _fontSize { 12.0 }; 69 qreal _textLineSpacing { 1.0 }; 70 QString _fontFamily; 71 72 public: CharFormat()73 CharFormat() {} 74 bool operator==(const CharFormat&) const; 75 style()76 FontStyle style() const { return _style; } setStyle(FontStyle s)77 void setStyle(FontStyle s) { _style = s; } bold()78 bool bold() const { return _style & FontStyle::Bold; } italic()79 bool italic() const { return _style & FontStyle::Italic; } underline()80 bool underline() const { return _style & FontStyle::Underline; } setBold(bool val)81 void setBold(bool val) { _style = val ? _style + FontStyle::Bold : _style - FontStyle::Bold; } setItalic(bool val)82 void setItalic(bool val) { _style = val ? _style + FontStyle::Italic : _style - FontStyle::Italic; } setUnderline(bool val)83 void setUnderline(bool val) { _style = val ? _style + FontStyle::Underline : _style - FontStyle::Underline; } 84 preedit()85 bool preedit() const { return _preedit; } valign()86 VerticalAlignment valign() const { return _valign; } fontSize()87 qreal fontSize() const { return _fontSize; } fontFamily()88 QString fontFamily() const { return _fontFamily; } setPreedit(bool val)89 void setPreedit(bool val) { _preedit = val; } setValign(VerticalAlignment val)90 void setValign(VerticalAlignment val) { _valign = val; } setFontSize(qreal val)91 void setFontSize(qreal val) { Q_ASSERT(val > 0.0); _fontSize = val; } setTextLineSpacing(qreal val)92 void setTextLineSpacing(qreal val) { _textLineSpacing = val; } setFontFamily(const QString & val)93 void setFontFamily(const QString& val) { _fontFamily = val; } 94 95 void setFormat(FormatId, QVariant); 96 }; 97 98 //--------------------------------------------------------- 99 // TextCursor 100 // Contains current position and start of selection 101 // during editing. 102 //--------------------------------------------------------- 103 104 class TextCursor { 105 TextBase* _text; 106 CharFormat _format; 107 int _row { 0 }; 108 int _column { 0 }; 109 int _selectLine { 0 }; // start of selection 110 int _selectColumn { 0 }; 111 112 public: TextCursor(TextBase * t)113 TextCursor(TextBase* t) : _text(t) {} 114 text()115 TextBase* text() const { return _text; } hasSelection()116 bool hasSelection() const { return (_selectLine != _row) || (_selectColumn != _column); } 117 void clearSelection(); 118 format()119 CharFormat* format() { return &_format; } format()120 const CharFormat* format() const { return &_format; } setFormat(const CharFormat & f)121 void setFormat(const CharFormat& f) { _format = f; } 122 row()123 int row() const { return _row; } column()124 int column() const { return _column; } selectLine()125 int selectLine() const { return _selectLine; } selectColumn()126 int selectColumn() const { return _selectColumn; } setRow(int val)127 void setRow(int val) { _row = val; } setColumn(int val)128 void setColumn(int val) { _column = val; } setSelectLine(int val)129 void setSelectLine(int val) { _selectLine = val; } setSelectColumn(int val)130 void setSelectColumn(int val) { _selectColumn = val; } 131 int columns() const; 132 void init(); 133 134 TextBlock& curLine() const; 135 QRectF cursorRect() const; 136 bool movePosition(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode = QTextCursor::MoveAnchor, int count = 1); 137 void doubleClickSelect(); moveCursorToEnd()138 void moveCursorToEnd() { movePosition(QTextCursor::End); } moveCursorToStart()139 void moveCursorToStart() { movePosition(QTextCursor::Start); } 140 QChar currentCharacter() const; 141 QString currentWord() const; 142 QString currentLine() const; 143 bool set(const QPointF& p, QTextCursor::MoveMode mode = QTextCursor::MoveAnchor); 144 QString selectedText() const; 145 QString extractText(int r1, int c1, int r2, int c2) const; 146 void updateCursorFormat(); 147 void setFormat(FormatId, QVariant); 148 void changeSelectionFormat(FormatId id, QVariant val); 149 150 private: 151 QString accessibleCurrentCharacter() const; 152 void accessibileMessage(QString& accMsg, int oldRow, int oldCol, QString oldSelection, QTextCursor::MoveMode mode) const; 153 }; 154 155 //--------------------------------------------------------- 156 // TextFragment 157 // contains a styled text 158 //--------------------------------------------------------- 159 160 class TextFragment { 161 162 public: 163 mutable CharFormat format; 164 QPointF pos; // y is relative to TextBlock->y() 165 mutable QString text; 166 167 bool operator ==(const TextFragment& f) const; 168 169 TextFragment(); 170 TextFragment(const QString& s); 171 TextFragment(TextCursor*, const QString&); 172 TextFragment split(int column); 173 void draw(QPainter*, const TextBase*) const; 174 QFont font(const TextBase*) const; 175 int columns() const; 176 void changeFormat(FormatId id, QVariant data); 177 }; 178 179 //--------------------------------------------------------- 180 // TextBlock 181 // represents a block of formatted text 182 //--------------------------------------------------------- 183 184 class TextBlock { 185 QList<TextFragment> _fragments; 186 qreal _y = 0; 187 qreal _lineSpacing; 188 QRectF _bbox; 189 bool _eol = false; 190 191 void simplify(); 192 193 public: TextBlock()194 TextBlock() {} 195 bool operator ==(const TextBlock& x) { return _fragments == x._fragments; } 196 bool operator !=(const TextBlock& x) { return _fragments != x._fragments; } 197 void draw(QPainter*, const TextBase*) const; 198 void layout(TextBase*); fragments()199 const QList<TextFragment>& fragments() const { return _fragments; } fragments()200 QList<TextFragment>& fragments() { return _fragments; } 201 QList<TextFragment>* fragmentsWithoutEmpty(); boundingRect()202 const QRectF& boundingRect() const { return _bbox; } 203 QRectF boundingRect(int col1, int col2, const TextBase*) const; 204 int columns() const; 205 void insert(TextCursor*, const QString&); 206 void insertEmptyFragmentIfNeeded(TextCursor*); 207 void removeEmptyFragment(); 208 QString remove(int column, TextCursor*); 209 QString remove(int start, int n, TextCursor*); 210 int column(qreal x, TextBase*) const; 211 TextBlock split(int column, TextCursor* cursor); 212 qreal xpos(int col, const TextBase*) const; 213 const CharFormat* formatAt(int) const; 214 const TextFragment* fragment(int col) const; 215 QList<TextFragment>::iterator fragment(int column, int* rcol, int* ridx); y()216 qreal y() const { return _y; } setY(qreal val)217 void setY(qreal val) { _y = val; } lineSpacing()218 qreal lineSpacing() const { return _lineSpacing; } 219 QString text(int, int) const; eol()220 bool eol() const { return _eol; } setEol(bool val)221 void setEol(bool val) { _eol = val; } 222 void changeFormat(FormatId, QVariant val, int start, int n); 223 }; 224 225 //--------------------------------------------------------- 226 // TextBase 227 //--------------------------------------------------------- 228 229 class TextBase : public Element { 230 // sorted by size to allow for most compact memory layout 231 M_PROPERTY(FontStyle, fontStyle, setFontStyle) 232 M_PROPERTY(Align, align, setAlign) 233 M_PROPERTY(FrameType, frameType, setFrameType) 234 M_PROPERTY(QString, family, setFamily) 235 M_PROPERTY(qreal, size, setSize) 236 M_PROPERTY(qreal, textLineSpacing, setTextLineSpacing) 237 M_PROPERTY(QColor, bgColor, setBgColor) 238 M_PROPERTY(QColor, frameColor, setFrameColor) 239 M_PROPERTY(Spatium, frameWidth, setFrameWidth) 240 M_PROPERTY(Spatium, paddingWidth, setPaddingWidth) 241 M_PROPERTY(int, frameRound, setFrameRound) 242 243 // there are two representations of text; only one 244 // might be valid and the other can be constructed from it 245 246 mutable QString _text; // cached 247 mutable bool textInvalid { true }; 248 249 QList<TextBlock> _layout; 250 bool layoutInvalid { true }; 251 Tid _tid; // text style id 252 253 QString preEdit; // move to EditData? 254 bool _layoutToParentWidth { false }; 255 256 int hexState { -1 }; 257 bool _primed { 0 }; 258 259 void drawSelection(QPainter*, const QRectF&) const; 260 void insert(TextCursor*, uint code); 261 void genText() const; 262 virtual int getPropertyFlagsIdx(Pid id) const override; 263 QString stripText(bool, bool, bool) const; 264 Sid offsetSid() const; 265 266 protected: 267 QColor textColor() const; 268 QRectF frame; // calculated in layout() 269 void layoutFrame(); 270 void layoutEdit(); 271 void createLayout(); 272 void insertSym(EditData& ed, SymId id); 273 274 public: 275 TextBase(Score* = 0, Tid tid = Tid::DEFAULT, ElementFlags = ElementFlag::NOTHING); 276 TextBase(Score*, ElementFlags); 277 TextBase(const TextBase&); 278 279 virtual bool mousePress(EditData&) override; 280 281 Text &operator=(const Text&) = delete; 282 283 virtual void draw(QPainter*) const override; 284 virtual void drawEditMode(QPainter* p, EditData& ed) override; 285 static void drawTextWorkaround(QPainter* p, QFont& f, const QPointF pos, const QString text); 286 plainToXmlText(const QString & s)287 static QString plainToXmlText(const QString& s) { return s.toHtmlEscaped(); } setPlainText(const QString & t)288 void setPlainText(const QString& t) { setXmlText(plainToXmlText(t)); } 289 virtual void setXmlText(const QString&); 290 QString xmlText() const; 291 QString plainText() const; 292 293 void insertText(EditData&, const QString&); 294 295 virtual void layout() override; 296 virtual void layout1(); 297 qreal lineSpacing() const; 298 qreal lineHeight() const; 299 virtual qreal baseLine() const override; 300 empty()301 bool empty() const { return xmlText().isEmpty(); } clear()302 void clear() { setXmlText(QString()); } 303 layoutToParentWidth()304 bool layoutToParentWidth() const { return _layoutToParentWidth; } setLayoutToParentWidth(bool v)305 void setLayoutToParentWidth(bool v) { _layoutToParentWidth = v; } 306 307 virtual void startEdit(EditData&) override; 308 virtual bool edit(EditData&) override; 309 virtual void editCut(EditData&) override; 310 virtual void editCopy(EditData&) override; 311 virtual void endEdit(EditData&) override; 312 void movePosition(EditData&, QTextCursor::MoveOperation); 313 314 bool deleteSelectedText(EditData&); 315 316 void selectAll(TextCursor*); 317 void multiClickSelect(EditData&, MultiClick); isPrimed()318 bool isPrimed() const { return _primed; } setPrimed(bool primed)319 void setPrimed(bool primed) { _primed = primed; } 320 321 virtual void write(XmlWriter& xml) const override; 322 virtual void read(XmlReader&) override; writeProperties(XmlWriter & xml)323 virtual void writeProperties(XmlWriter& xml) const { writeProperties(xml, true, true); } writeProperties(XmlWriter & xml,bool writeText)324 void writeProperties(XmlWriter& xml, bool writeText) const { writeProperties(xml, writeText, true); } 325 void writeProperties(XmlWriter&, bool, bool) const; 326 bool readProperties(XmlReader&); 327 328 virtual void paste(EditData&); 329 330 QRectF pageRectangle() const; 331 332 void dragTo(EditData&); 333 334 QVector<QLineF> dragAnchorLines() const override; 335 336 virtual bool acceptDrop(EditData&) const override; 337 virtual Element* drop(EditData&) override; 338 339 friend class TextBlock; 340 friend class TextFragment; 341 QString convertFromHtml(const QString& ss) const; 342 static QString convertToHtml(const QString&, const TextStyle&); 343 static QString tagEscape(QString s); 344 static QString unEscape(QString s); 345 static QString escape(QString s); 346 347 virtual QString accessibleInfo() const override; 348 virtual QString screenReaderInfo() const override; 349 350 virtual int subtype() const override; 351 virtual QString subtypeName() const override; 352 353 QList<TextFragment> fragmentList() const; // for MusicXML formatted export 354 355 static bool validateText(QString& s); inHexState()356 bool inHexState() const { return hexState >= 0; } 357 void endHexState(EditData&); 358 void inputTransition(EditData&, QInputMethodEvent*); 359 360 QFont font() const; 361 QFontMetricsF fontMetrics() const; 362 363 virtual QVariant getProperty(Pid propertyId) const override; 364 virtual bool setProperty(Pid propertyId, const QVariant& v) override; 365 virtual QVariant propertyDefault(Pid id) const override; 366 virtual void undoChangeProperty(Pid id, const QVariant& v, PropertyFlags ps) override; 367 virtual Pid propertyId(const QStringRef& xmlName) const override; 368 virtual Sid getPropertyStyle(Pid) const; 369 virtual void styleChanged(); 370 void editInsertText(TextCursor*, const QString&); 371 372 TextCursor* cursor(const EditData&); textBlock(int line)373 const TextBlock& textBlock(int line) const { return _layout[line]; } textBlock(int line)374 TextBlock& textBlock(int line) { return _layout[line]; } textBlockList()375 QList<TextBlock>& textBlockList() { return _layout; } rows()376 int rows() const { return _layout.size(); } 377 setTextInvalid()378 void setTextInvalid() { textInvalid = true; } isTextInvalid()379 bool isTextInvalid() const { return textInvalid; } setLayoutInvalid()380 void setLayoutInvalid() { layoutInvalid = true; } isLayoutInvalid()381 bool isLayoutInvalid() const { return layoutInvalid; } 382 383 // helper functions hasFrame()384 bool hasFrame() const { return _frameType != FrameType::NO_FRAME; } circle()385 bool circle() const { return _frameType == FrameType::CIRCLE; } square()386 bool square() const { return _frameType == FrameType::SQUARE; } 387 tid()388 Tid tid() const { return _tid; } setTid(Tid id)389 void setTid(Tid id) { _tid = id; } 390 void initTid(Tid id); 391 void initTid(Tid id, bool preserveDifferent); 392 virtual void initElementStyle(const ElementStyle*) override; 393 bold()394 bool bold() const { return _fontStyle & FontStyle::Bold; } italic()395 bool italic() const { return _fontStyle & FontStyle::Italic; } underline()396 bool underline() const { return _fontStyle & FontStyle::Underline; } setBold(bool val)397 void setBold(bool val) { _fontStyle = val ? _fontStyle + FontStyle::Bold : _fontStyle - FontStyle::Bold; } setItalic(bool val)398 void setItalic(bool val) { _fontStyle = val ? _fontStyle + FontStyle::Italic : _fontStyle - FontStyle::Italic; } setUnderline(bool val)399 void setUnderline(bool val) { _fontStyle = val ? _fontStyle + FontStyle::Underline : _fontStyle - FontStyle::Underline; } 400 401 bool hasCustomFormatting() const; 402 403 friend class TextCursor; 404 using ScoreElement::undoChangeProperty; 405 }; 406 407 } // namespace Ms 408 409 #endif 410