OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / texteditor / basetextdocumentlayout.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #include "basetextdocumentlayout.h"
34
35 using namespace TextEditor;
36
37 CodeFormatterData::~CodeFormatterData()
38 {
39 }
40
41 TextBlockUserData::~TextBlockUserData()
42 {
43     TextMarks marks = m_marks;
44     m_marks.clear();
45     foreach (ITextMark *mrk, marks) {
46         mrk->removedFromEditor();
47     }
48
49     if (m_codeFormatterData)
50         delete m_codeFormatterData;
51 }
52
53 int TextBlockUserData::braceDepthDelta() const
54 {
55     int delta = 0;
56     for (int i = 0; i < m_parentheses.size(); ++i) {
57         switch (m_parentheses.at(i).chr.unicode()) {
58         case '{': case '+': case '[': ++delta; break;
59         case '}': case '-': case ']': --delta; break;
60         default: break;
61         }
62     }
63     return delta;
64 }
65
66 TextBlockUserData::MatchType TextBlockUserData::checkOpenParenthesis(QTextCursor *cursor, QChar c)
67 {
68     QTextBlock block = cursor->block();
69     if (!BaseTextDocumentLayout::hasParentheses(block) || BaseTextDocumentLayout::ifdefedOut(block))
70         return NoMatch;
71
72     Parentheses parenList = BaseTextDocumentLayout::parentheses(block);
73     Parenthesis openParen, closedParen;
74     QTextBlock closedParenParag = block;
75
76     const int cursorPos = cursor->position() - closedParenParag.position();
77     int i = 0;
78     int ignore = 0;
79     bool foundOpen = false;
80     for (;;) {
81         if (!foundOpen) {
82             if (i >= parenList.count())
83                 return NoMatch;
84             openParen = parenList.at(i);
85             if (openParen.pos != cursorPos) {
86                 ++i;
87                 continue;
88             } else {
89                 foundOpen = true;
90                 ++i;
91             }
92         }
93
94         if (i >= parenList.count()) {
95             for (;;) {
96                 closedParenParag = closedParenParag.next();
97                 if (!closedParenParag.isValid())
98                     return NoMatch;
99                 if (BaseTextDocumentLayout::hasParentheses(closedParenParag)
100                     && !BaseTextDocumentLayout::ifdefedOut(closedParenParag)) {
101                     parenList = BaseTextDocumentLayout::parentheses(closedParenParag);
102                     break;
103                 }
104             }
105             i = 0;
106         }
107
108         closedParen = parenList.at(i);
109         if (closedParen.type == Parenthesis::Opened) {
110             ignore++;
111             ++i;
112             continue;
113         } else {
114             if (ignore > 0) {
115                 ignore--;
116                 ++i;
117                 continue;
118             }
119
120             cursor->clearSelection();
121             cursor->setPosition(closedParenParag.position() + closedParen.pos + 1, QTextCursor::KeepAnchor);
122
123             if ((c == QLatin1Char('{') && closedParen.chr != QLatin1Char('}'))
124                 || (c == QLatin1Char('(') && closedParen.chr != QLatin1Char(')'))
125                 || (c == QLatin1Char('[') && closedParen.chr != QLatin1Char(']'))
126                 || (c == QLatin1Char('+') && closedParen.chr != QLatin1Char('-'))
127                )
128                 return Mismatch;
129
130             return Match;
131         }
132     }
133 }
134
135 TextBlockUserData::MatchType TextBlockUserData::checkClosedParenthesis(QTextCursor *cursor, QChar c)
136 {
137     QTextBlock block = cursor->block();
138     if (!BaseTextDocumentLayout::hasParentheses(block) || BaseTextDocumentLayout::ifdefedOut(block))
139         return NoMatch;
140
141     Parentheses parenList = BaseTextDocumentLayout::parentheses(block);
142     Parenthesis openParen, closedParen;
143     QTextBlock openParenParag = block;
144
145     const int cursorPos = cursor->position() - openParenParag.position();
146     int i = parenList.count() - 1;
147     int ignore = 0;
148     bool foundClosed = false;
149     for (;;) {
150         if (!foundClosed) {
151             if (i < 0)
152                 return NoMatch;
153             closedParen = parenList.at(i);
154             if (closedParen.pos != cursorPos - 1) {
155                 --i;
156                 continue;
157             } else {
158                 foundClosed = true;
159                 --i;
160             }
161         }
162
163         if (i < 0) {
164             for (;;) {
165                 openParenParag = openParenParag.previous();
166                 if (!openParenParag.isValid())
167                     return NoMatch;
168
169                 if (BaseTextDocumentLayout::hasParentheses(openParenParag)
170                     && !BaseTextDocumentLayout::ifdefedOut(openParenParag)) {
171                     parenList = BaseTextDocumentLayout::parentheses(openParenParag);
172                     break;
173                 }
174             }
175             i = parenList.count() - 1;
176         }
177
178         openParen = parenList.at(i);
179         if (openParen.type == Parenthesis::Closed) {
180             ignore++;
181             --i;
182             continue;
183         } else {
184             if (ignore > 0) {
185                 ignore--;
186                 --i;
187                 continue;
188             }
189
190             cursor->clearSelection();
191             cursor->setPosition(openParenParag.position() + openParen.pos, QTextCursor::KeepAnchor);
192
193             if ((c == '}' && openParen.chr != '{')    ||
194                  (c == ')' && openParen.chr != '(')   ||
195                  (c == ']' && openParen.chr != '[')   ||
196                  (c == '-' && openParen.chr != '+'))
197                 return Mismatch;
198
199             return Match;
200         }
201     }
202 }
203
204 bool TextBlockUserData::findPreviousOpenParenthesis(QTextCursor *cursor, bool select)
205 {
206     QTextBlock block = cursor->block();
207     int position = cursor->position();
208     int ignore = 0;
209     while (block.isValid()) {
210         Parentheses parenList = BaseTextDocumentLayout::parentheses(block);
211         if (!parenList.isEmpty() && !BaseTextDocumentLayout::ifdefedOut(block)) {
212             for (int i = parenList.count()-1; i >= 0; --i) {
213                 Parenthesis paren = parenList.at(i);
214                 if (block == cursor->block() &&
215                     (position - block.position() <= paren.pos + (paren.type == Parenthesis::Closed ? 1 : 0)))
216                         continue;
217                 if (paren.type == Parenthesis::Closed) {
218                     ++ignore;
219                 } else if (ignore > 0) {
220                     --ignore;
221                 } else {
222                     cursor->setPosition(block.position() + paren.pos, select ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
223                     return true;
224                 }
225             }
226         }
227         block = block.previous();
228     }
229     return false;
230 }
231
232 bool TextBlockUserData::findPreviousBlockOpenParenthesis(QTextCursor *cursor, bool checkStartPosition)
233 {
234     QTextBlock block = cursor->block();
235     int position = cursor->position();
236     int ignore = 0;
237     while (block.isValid()) {
238         Parentheses parenList = BaseTextDocumentLayout::parentheses(block);
239         if (!parenList.isEmpty() && !BaseTextDocumentLayout::ifdefedOut(block)) {
240             for (int i = parenList.count()-1; i >= 0; --i) {
241                 Parenthesis paren = parenList.at(i);
242                 if (paren.chr != QLatin1Char('{') && paren.chr != QLatin1Char('}')
243                     && paren.chr != QLatin1Char('+') && paren.chr != QLatin1Char('-')
244                     && paren.chr != QLatin1Char('[') && paren.chr != QLatin1Char(']'))
245                     continue;
246                 if (block == cursor->block()) {
247                     if (position - block.position() <= paren.pos + (paren.type == Parenthesis::Closed ? 1 : 0))
248                         continue;
249                     if (checkStartPosition && paren.type == Parenthesis::Opened && paren.pos== cursor->position()) {
250                         return true;
251                     }
252                 }
253                 if (paren.type == Parenthesis::Closed) {
254                     ++ignore;
255                 } else if (ignore > 0) {
256                     --ignore;
257                 } else {
258                     cursor->setPosition(block.position() + paren.pos);
259                     return true;
260                 }
261             }
262         }
263         block = block.previous();
264     }
265     return false;
266 }
267
268 bool TextBlockUserData::findNextClosingParenthesis(QTextCursor *cursor, bool select)
269 {
270     QTextBlock block = cursor->block();
271     int position = cursor->position();
272     int ignore = 0;
273     while (block.isValid()) {
274         Parentheses parenList = BaseTextDocumentLayout::parentheses(block);
275         if (!parenList.isEmpty() && !BaseTextDocumentLayout::ifdefedOut(block)) {
276             for (int i = 0; i < parenList.count(); ++i) {
277                 Parenthesis paren = parenList.at(i);
278                 if (block == cursor->block() &&
279                     (position - block.position() > paren.pos - (paren.type == Parenthesis::Opened ? 1 : 0)))
280                     continue;
281                 if (paren.type == Parenthesis::Opened) {
282                     ++ignore;
283                 } else if (ignore > 0) {
284                     --ignore;
285                 } else {
286                     cursor->setPosition(block.position() + paren.pos+1, select ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
287                     return true;
288                 }
289             }
290         }
291         block = block.next();
292     }
293     return false;
294 }
295
296 bool TextBlockUserData::findNextBlockClosingParenthesis(QTextCursor *cursor)
297 {
298     QTextBlock block = cursor->block();
299     int position = cursor->position();
300     int ignore = 0;
301     while (block.isValid()) {
302         Parentheses parenList = BaseTextDocumentLayout::parentheses(block);
303         if (!parenList.isEmpty() && !BaseTextDocumentLayout::ifdefedOut(block)) {
304             for (int i = 0; i < parenList.count(); ++i) {
305                 Parenthesis paren = parenList.at(i);
306                 if (paren.chr != QLatin1Char('{') && paren.chr != QLatin1Char('}')
307                     && paren.chr != QLatin1Char('+') && paren.chr != QLatin1Char('-')
308                     && paren.chr != QLatin1Char('[') && paren.chr != QLatin1Char(']'))
309                     continue;
310                 if (block == cursor->block() &&
311                     (position - block.position() > paren.pos - (paren.type == Parenthesis::Opened ? 1 : 0)))
312                     continue;
313                 if (paren.type == Parenthesis::Opened) {
314                     ++ignore;
315                 } else if (ignore > 0) {
316                     --ignore;
317                 } else {
318                     cursor->setPosition(block.position() + paren.pos+1);
319                     return true;
320                 }
321             }
322         }
323         block = block.next();
324     }
325     return false;
326 }
327
328 TextBlockUserData::MatchType TextBlockUserData::matchCursorBackward(QTextCursor *cursor)
329 {
330     cursor->clearSelection();
331     const QTextBlock block = cursor->block();
332
333     if (!BaseTextDocumentLayout::hasParentheses(block) || BaseTextDocumentLayout::ifdefedOut(block))
334         return NoMatch;
335
336     const int relPos = cursor->position() - block.position();
337
338     Parentheses parentheses = BaseTextDocumentLayout::parentheses(block);
339     const Parentheses::const_iterator cend = parentheses.constEnd();
340     for (Parentheses::const_iterator it = parentheses.constBegin();it != cend; ++it) {
341         const Parenthesis &paren = *it;
342         if (paren.pos == relPos - 1
343             && paren.type == Parenthesis::Closed) {
344             return checkClosedParenthesis(cursor, paren.chr);
345         }
346     }
347     return NoMatch;
348 }
349
350 TextBlockUserData::MatchType TextBlockUserData::matchCursorForward(QTextCursor *cursor)
351 {
352     cursor->clearSelection();
353     const QTextBlock block = cursor->block();
354
355     if (!BaseTextDocumentLayout::hasParentheses(block) || BaseTextDocumentLayout::ifdefedOut(block))
356         return NoMatch;
357
358     const int relPos = cursor->position() - block.position();
359
360     Parentheses parentheses = BaseTextDocumentLayout::parentheses(block);
361     const Parentheses::const_iterator cend = parentheses.constEnd();
362     for (Parentheses::const_iterator it = parentheses.constBegin();it != cend; ++it) {
363         const Parenthesis &paren = *it;
364         if (paren.pos == relPos
365             && paren.type == Parenthesis::Opened) {
366             return checkOpenParenthesis(cursor, paren.chr);
367         }
368     }
369     return NoMatch;
370 }
371
372 void TextBlockUserData::setCodeFormatterData(CodeFormatterData *data)
373 {
374     if (m_codeFormatterData)
375         delete m_codeFormatterData;
376
377     m_codeFormatterData = data;
378 }
379
380 void TextBlockUserData::addMark(ITextMark *mark)
381 {
382     int i = 0;
383     for ( ; i < m_marks.size(); ++i) {
384         if (mark->priority() < m_marks.at(i)->priority())
385             break;
386     }
387     m_marks.insert(i, mark);
388 }
389
390
391 BaseTextDocumentLayout::BaseTextDocumentLayout(QTextDocument *doc)
392     :QPlainTextDocumentLayout(doc) {
393     lastSaveRevision = 0;
394     hasMarks = 0;
395     m_requiredWidth = 0;
396 }
397
398 BaseTextDocumentLayout::~BaseTextDocumentLayout()
399 {
400 }
401
402 void BaseTextDocumentLayout::setParentheses(const QTextBlock &block, const Parentheses &parentheses)
403 {
404     if (parentheses.isEmpty()) {
405         if (TextBlockUserData *userData = testUserData(block))
406             userData->clearParentheses();
407     } else {
408         userData(block)->setParentheses(parentheses);
409     }
410 }
411
412 Parentheses BaseTextDocumentLayout::parentheses(const QTextBlock &block)
413 {
414     if (TextBlockUserData *userData = testUserData(block))
415         return userData->parentheses();
416     return Parentheses();
417 }
418
419 bool BaseTextDocumentLayout::hasParentheses(const QTextBlock &block)
420 {
421     if (TextBlockUserData *userData = testUserData(block))
422         return userData->hasParentheses();
423     return false;
424 }
425
426 bool BaseTextDocumentLayout::setIfdefedOut(const QTextBlock &block)
427 {
428     return userData(block)->setIfdefedOut();
429 }
430
431 bool BaseTextDocumentLayout::clearIfdefedOut(const QTextBlock &block)
432 {
433     if (TextBlockUserData *userData = testUserData(block))
434         return userData->clearIfdefedOut();
435     return false;
436 }
437
438 bool BaseTextDocumentLayout::ifdefedOut(const QTextBlock &block)
439 {
440     if (TextBlockUserData *userData = testUserData(block))
441         return userData->ifdefedOut();
442     return false;
443 }
444
445 int BaseTextDocumentLayout::braceDepthDelta(const QTextBlock &block)
446 {
447     if (TextBlockUserData *userData = testUserData(block))
448         return userData->braceDepthDelta();
449     return 0;
450 }
451
452 int BaseTextDocumentLayout::braceDepth(const QTextBlock &block)
453 {
454     int state = block.userState();
455     if (state == -1)
456         return 0;
457     return state >> 8;
458 }
459
460 void BaseTextDocumentLayout::setBraceDepth(QTextBlock &block, int depth)
461 {
462     int state = block.userState();
463     if (state == -1)
464         state = 0;
465     state = state & 0xff;
466     block.setUserState((depth << 8) | state);
467 }
468
469 void BaseTextDocumentLayout::changeBraceDepth(QTextBlock &block, int delta)
470 {
471     if (delta)
472         setBraceDepth(block, braceDepth(block) + delta);
473 }
474
475 void BaseTextDocumentLayout::setLexerState(const QTextBlock &block, int state)
476 {
477     if (state == 0) {
478         if (TextBlockUserData *userData = testUserData(block))
479             userData->setLexerState(0);
480     } else {
481         userData(block)->setLexerState(qMax(0,state));
482     }
483 }
484
485 int BaseTextDocumentLayout::lexerState(const QTextBlock &block)
486 {
487     if (TextBlockUserData *userData = testUserData(block))
488         return userData->lexerState();
489     return 0;
490 }
491
492 void BaseTextDocumentLayout::setFoldingIndent(const QTextBlock &block, int indent)
493 {
494     if (indent == 0) {
495         if (TextBlockUserData *userData = testUserData(block))
496             userData->setFoldingIndent(0);
497     } else {
498         userData(block)->setFoldingIndent(qMax(0,indent));
499     }
500 }
501
502 int BaseTextDocumentLayout::foldingIndent(const QTextBlock &block)
503 {
504     if (TextBlockUserData *userData = testUserData(block))
505         return userData->foldingIndent();
506     return 0;
507 }
508
509 void BaseTextDocumentLayout::changeFoldingIndent(QTextBlock &block, int delta)
510 {
511     if (delta)
512         setFoldingIndent(block, foldingIndent(block) + delta);
513 }
514
515 bool BaseTextDocumentLayout::canFold(const QTextBlock &block)
516 {
517     return (block.next().isValid() && foldingIndent(block.next()) > foldingIndent(block));
518 }
519
520 bool BaseTextDocumentLayout::isFolded(const QTextBlock &block)
521 {
522     if (TextBlockUserData *userData = testUserData(block))
523         return userData->folded();
524     return false;
525 }
526
527 void BaseTextDocumentLayout::setFolded(const QTextBlock &block, bool folded)
528 {
529     if (folded)
530         userData(block)->setFolded(true);
531     else {
532         if (TextBlockUserData *userData = testUserData(block))
533             return userData->setFolded(false);
534     }
535 }
536
537 void BaseTextDocumentLayout::doFoldOrUnfold(const QTextBlock& block, bool unfold)
538 {
539     if (!canFold(block))
540         return;
541     QTextBlock b = block.next();
542
543     int indent = foldingIndent(block);
544     while (b.isValid() && foldingIndent(b) > indent && (unfold || b.next().isValid())) {
545         b.setVisible(unfold);
546         b.setLineCount(unfold? qMax(1, b.layout()->lineCount()) : 0);
547         if (unfold) { // do not unfold folded sub-blocks
548             if (isFolded(b) && b.next().isValid()) {
549                 int jndent = foldingIndent(b);
550                 b = b.next();
551                 while (b.isValid() && foldingIndent(b) > jndent)
552                     b = b.next();
553                 continue;
554             }
555         }
556         b = b.next();
557     }
558     setFolded(block, !unfold);
559 }
560
561 void BaseTextDocumentLayout::setRequiredWidth(int width)
562 {
563     int oldw = m_requiredWidth;
564     m_requiredWidth = width;
565     int dw = QPlainTextDocumentLayout::documentSize().width();
566     if (oldw > dw || width > dw)
567         emitDocumentSizeChanged();
568 }
569
570
571 QSizeF BaseTextDocumentLayout::documentSize() const
572 {
573     QSizeF size = QPlainTextDocumentLayout::documentSize();
574     size.setWidth(qMax((qreal)m_requiredWidth, size.width()));
575     return size;
576 }