1 /***************************************************************************
2 * Copyright (C) 2005-08 by the Quassel Project *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include <QFontMetrics>
22 #include <QTextBoundaryFinder>
24 #include "chatlinemodelitem.h"
25 #include "chatlinemodel.h"
29 // This Struct is taken from Harfbuzz. We use it only to calc it's size.
30 // we use a shared memory region so we do not have to malloc a buffer area for every line
32 /*HB_LineBreakType*/ unsigned lineBreakType :2;
33 /*HB_Bool*/ unsigned whiteSpace :1; /* A unicode whitespace character, except NBSP, ZWNBSP */
34 /*HB_Bool*/ unsigned charStop :1; /* Valid cursor position (for left/right arrow) */
35 /*HB_Bool*/ unsigned wordBoundary :1;
36 /*HB_Bool*/ unsigned sentenceBoundary :1;
38 } HB_CharAttributes_Dummy;
40 unsigned char *ChatLineModelItem::TextBoundaryFinderBuffer = (unsigned char *)malloc(512 * sizeof(HB_CharAttributes_Dummy));
41 int ChatLineModelItem::TextBoundaryFinderBufferSize = 512 * (sizeof(HB_CharAttributes_Dummy) / sizeof(unsigned char));
43 ChatLineModelItem::ChatLineModelItem(const Message &msg)
44 : MessageModelItem(msg)
46 QtUiStyle::StyledMessage m = QtUi::style()->styleMessage(msg);
48 _timestamp.plainText = m.timestamp.plainText;
49 _sender.plainText = m.sender.plainText;
50 _contents.plainText = m.contents.plainText;
52 _timestamp.formatList = m.timestamp.formatList;
53 _sender.formatList = m.sender.formatList;
54 _contents.formatList = m.contents.formatList;
60 QVariant ChatLineModelItem::data(int column, int role) const {
61 const ChatLinePart *part = 0;
64 case ChatLineModel::TimestampColumn:
67 case ChatLineModel::SenderColumn:
70 case ChatLineModel::ContentsColumn:
74 return MessageModelItem::data(column, role);
78 case ChatLineModel::DisplayRole:
79 return part->plainText;
80 case ChatLineModel::FormatRole:
81 return QVariant::fromValue<UiStyle::FormatList>(part->formatList);
82 case ChatLineModel::WrapListRole:
83 if(column != ChatLineModel::ContentsColumn)
85 return QVariant::fromValue<ChatLineModel::WrapList>(_wrapList);
87 return MessageModelItem::data(column, role);
90 void ChatLineModelItem::computeWrapList() {
91 if(_contents.plainText.isEmpty())
94 enum Mode { SearchStart, SearchEnd };
96 QList<ChatLineModel::Word> wplist; // use a temp list which we'll later copy into a QVector for efficiency
97 // QTextBoundaryFinder finder(QTextBoundaryFinder::Word, _contents.plainText);
98 QTextBoundaryFinder finder(QTextBoundaryFinder::Word, _contents.plainText.unicode(), _contents.plainText.length(), TextBoundaryFinderBuffer, TextBoundaryFinderBufferSize);
102 bool wordStart = false;
103 bool wordEnd = false;
104 Mode mode = SearchEnd;
105 ChatLineModel::Word word;
109 QTextLayout layout(_contents.plainText);
111 option.setWrapMode(QTextOption::NoWrap);
112 layout.setTextOption(option);
114 layout.setAdditionalFormats(QtUi::style()->toTextLayoutList(_contents.formatList, _contents.plainText.length()));
115 layout.beginLayout();
116 QTextLine line = layout.createLine();
117 line.setNumColumns(_contents.plainText.length());
121 idx = finder.toNextBoundary();
123 idx = _contents.plainText.length();
128 wordStart = finder.boundaryReasons().testFlag(QTextBoundaryFinder::StartWord);
129 wordEnd = finder.boundaryReasons().testFlag(QTextBoundaryFinder::EndWord);
132 //if(flg) qDebug() << idx << mode << wordStart << wordEnd << _contents.plainText.left(idx) << _contents.plainText.mid(idx);
134 if(mode == SearchEnd || (!wordStart && wordEnd)) {
135 if(wordStart || !wordEnd) continue;
140 int wordendx = line.cursorToX(oldidx);
141 int trailingendx = line.cursorToX(idx);
142 word.width = wordendx - wordstartx;
143 word.trailing = trailingendx - wordendx;
144 wordstartx = trailingendx;
151 // the part " || (finder.position() == _contents.plainText.length())" shouldn't be necessary
152 // but in rare and indeterministic cases Qt states that the end of the text is not a boundary o_O
153 } while(finder.isAtBoundary() || (finder.position() == _contents.plainText.length()));
155 // A QVector needs less space than a QList
156 _wrapList.resize(wplist.count());
157 for(int i = 0; i < wplist.count(); i++) {
158 _wrapList[i] = wplist.at(i);