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