|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtGui module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
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 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "qtextbrowser.h" |
|
43 #include "qtextedit_p.h" |
|
44 |
|
45 #ifndef QT_NO_TEXTBROWSER |
|
46 |
|
47 #include <qstack.h> |
|
48 #include <qapplication.h> |
|
49 #include <qevent.h> |
|
50 #include <qdesktopwidget.h> |
|
51 #include <qdebug.h> |
|
52 #include <qabstracttextdocumentlayout.h> |
|
53 #include "private/qtextdocumentlayout_p.h" |
|
54 #include <qtextcodec.h> |
|
55 #include <qpainter.h> |
|
56 #include <qdir.h> |
|
57 #include <qwhatsthis.h> |
|
58 #include <qtextobject.h> |
|
59 #include <qdesktopservices.h> |
|
60 |
|
61 QT_BEGIN_NAMESPACE |
|
62 |
|
63 class QTextBrowserPrivate : public QTextEditPrivate |
|
64 { |
|
65 Q_DECLARE_PUBLIC(QTextBrowser) |
|
66 public: |
|
67 inline QTextBrowserPrivate() |
|
68 : textOrSourceChanged(false), forceLoadOnSourceChange(false), openExternalLinks(false), |
|
69 openLinks(true) |
|
70 #ifdef QT_KEYPAD_NAVIGATION |
|
71 , lastKeypadScrollValue(-1) |
|
72 #endif |
|
73 {} |
|
74 |
|
75 void init(); |
|
76 |
|
77 struct HistoryEntry { |
|
78 inline HistoryEntry() |
|
79 : hpos(0), vpos(0), focusIndicatorPosition(-1), |
|
80 focusIndicatorAnchor(-1) {} |
|
81 QUrl url; |
|
82 QString title; |
|
83 int hpos; |
|
84 int vpos; |
|
85 int focusIndicatorPosition, focusIndicatorAnchor; |
|
86 }; |
|
87 |
|
88 HistoryEntry history(int i) const |
|
89 { |
|
90 if (i <= 0) |
|
91 if (-i < stack.count()) |
|
92 return stack[stack.count()+i-1]; |
|
93 else |
|
94 return HistoryEntry(); |
|
95 else |
|
96 if (i <= forwardStack.count()) |
|
97 return forwardStack[forwardStack.count()-i]; |
|
98 else |
|
99 return HistoryEntry(); |
|
100 } |
|
101 |
|
102 |
|
103 HistoryEntry createHistoryEntry() const; |
|
104 void restoreHistoryEntry(const HistoryEntry entry); |
|
105 |
|
106 QStack<HistoryEntry> stack; |
|
107 QStack<HistoryEntry> forwardStack; |
|
108 QUrl home; |
|
109 QUrl currentURL; |
|
110 |
|
111 QStringList searchPaths; |
|
112 |
|
113 /*flag necessary to give the linkClicked() signal some meaningful |
|
114 semantics when somebody connected to it calls setText() or |
|
115 setSource() */ |
|
116 bool textOrSourceChanged; |
|
117 bool forceLoadOnSourceChange; |
|
118 |
|
119 bool openExternalLinks; |
|
120 bool openLinks; |
|
121 |
|
122 #ifndef QT_NO_CURSOR |
|
123 QCursor oldCursor; |
|
124 #endif |
|
125 |
|
126 QString findFile(const QUrl &name) const; |
|
127 |
|
128 inline void _q_documentModified() |
|
129 { |
|
130 textOrSourceChanged = true; |
|
131 forceLoadOnSourceChange = !currentURL.path().isEmpty(); |
|
132 } |
|
133 |
|
134 void _q_activateAnchor(const QString &href); |
|
135 void _q_highlightLink(const QString &href); |
|
136 |
|
137 void setSource(const QUrl &url); |
|
138 |
|
139 // re-imlemented from QTextEditPrivate |
|
140 virtual QUrl resolveUrl(const QUrl &url) const; |
|
141 inline QUrl resolveUrl(const QString &url) const |
|
142 { return resolveUrl(QUrl::fromEncoded(url.toUtf8())); } |
|
143 |
|
144 #ifdef QT_KEYPAD_NAVIGATION |
|
145 void keypadMove(bool next); |
|
146 QTextCursor prevFocus; |
|
147 int lastKeypadScrollValue; |
|
148 #endif |
|
149 }; |
|
150 |
|
151 QString QTextBrowserPrivate::findFile(const QUrl &name) const |
|
152 { |
|
153 QString fileName; |
|
154 if (name.scheme() == QLatin1String("qrc")) |
|
155 fileName = QLatin1String(":/") + name.path(); |
|
156 else |
|
157 fileName = name.toLocalFile(); |
|
158 |
|
159 if (QFileInfo(fileName).isAbsolute()) |
|
160 return fileName; |
|
161 |
|
162 foreach (QString path, searchPaths) { |
|
163 if (!path.endsWith(QLatin1Char('/'))) |
|
164 path.append(QLatin1Char('/')); |
|
165 path.append(fileName); |
|
166 if (QFileInfo(path).isReadable()) |
|
167 return path; |
|
168 } |
|
169 |
|
170 return fileName; |
|
171 } |
|
172 |
|
173 QUrl QTextBrowserPrivate::resolveUrl(const QUrl &url) const |
|
174 { |
|
175 if (!url.isRelative()) |
|
176 return url; |
|
177 |
|
178 // For the second case QUrl can merge "#someanchor" with "foo.html" |
|
179 // correctly to "foo.html#someanchor" |
|
180 if (!(currentURL.isRelative() |
|
181 || (currentURL.scheme() == QLatin1String("file") |
|
182 && !QFileInfo(currentURL.toLocalFile()).isAbsolute())) |
|
183 || (url.hasFragment() && url.path().isEmpty())) { |
|
184 return currentURL.resolved(url); |
|
185 } |
|
186 |
|
187 // this is our last resort when current url and new url are both relative |
|
188 // we try to resolve against the current working directory in the local |
|
189 // file system. |
|
190 QFileInfo fi(currentURL.toLocalFile()); |
|
191 if (fi.exists()) { |
|
192 return QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(url); |
|
193 } |
|
194 |
|
195 return url; |
|
196 } |
|
197 |
|
198 void QTextBrowserPrivate::_q_activateAnchor(const QString &href) |
|
199 { |
|
200 if (href.isEmpty()) |
|
201 return; |
|
202 Q_Q(QTextBrowser); |
|
203 |
|
204 #ifndef QT_NO_CURSOR |
|
205 viewport->setCursor(oldCursor); |
|
206 #endif |
|
207 |
|
208 const QUrl url = resolveUrl(href); |
|
209 |
|
210 if (!openLinks) { |
|
211 emit q->anchorClicked(url); |
|
212 return; |
|
213 } |
|
214 |
|
215 textOrSourceChanged = false; |
|
216 |
|
217 #ifndef QT_NO_DESKTOPSERVICES |
|
218 if ((openExternalLinks |
|
219 && url.scheme() != QLatin1String("file") |
|
220 && url.scheme() != QLatin1String("qrc") |
|
221 && !url.isRelative()) |
|
222 || (url.isRelative() && !currentURL.isRelative() |
|
223 && currentURL.scheme() != QLatin1String("file") |
|
224 && currentURL.scheme() != QLatin1String("qrc"))) { |
|
225 QDesktopServices::openUrl(url); |
|
226 return; |
|
227 } |
|
228 #endif |
|
229 |
|
230 emit q->anchorClicked(url); |
|
231 |
|
232 if (textOrSourceChanged) |
|
233 return; |
|
234 |
|
235 q->setSource(url); |
|
236 } |
|
237 |
|
238 void QTextBrowserPrivate::_q_highlightLink(const QString &anchor) |
|
239 { |
|
240 Q_Q(QTextBrowser); |
|
241 if (anchor.isEmpty()) { |
|
242 #ifndef QT_NO_CURSOR |
|
243 if (viewport->cursor().shape() != Qt::PointingHandCursor) |
|
244 oldCursor = viewport->cursor(); |
|
245 viewport->setCursor(oldCursor); |
|
246 #endif |
|
247 emit q->highlighted(QUrl()); |
|
248 emit q->highlighted(QString()); |
|
249 } else { |
|
250 #ifndef QT_NO_CURSOR |
|
251 viewport->setCursor(Qt::PointingHandCursor); |
|
252 #endif |
|
253 |
|
254 const QUrl url = resolveUrl(anchor); |
|
255 emit q->highlighted(url); |
|
256 // convenience to ease connecting to QStatusBar::showMessage(const QString &) |
|
257 emit q->highlighted(url.toString()); |
|
258 } |
|
259 } |
|
260 |
|
261 void QTextBrowserPrivate::setSource(const QUrl &url) |
|
262 { |
|
263 Q_Q(QTextBrowser); |
|
264 #ifndef QT_NO_CURSOR |
|
265 if (q->isVisible()) |
|
266 QApplication::setOverrideCursor(Qt::WaitCursor); |
|
267 #endif |
|
268 textOrSourceChanged = true; |
|
269 |
|
270 QString txt; |
|
271 |
|
272 bool doSetText = false; |
|
273 |
|
274 QUrl currentUrlWithoutFragment = currentURL; |
|
275 currentUrlWithoutFragment.setFragment(QString()); |
|
276 QUrl newUrlWithoutFragment = currentURL.resolved(url); |
|
277 newUrlWithoutFragment.setFragment(QString()); |
|
278 |
|
279 if (url.isValid() |
|
280 && (newUrlWithoutFragment != currentUrlWithoutFragment || forceLoadOnSourceChange)) { |
|
281 QVariant data = q->loadResource(QTextDocument::HtmlResource, resolveUrl(url)); |
|
282 if (data.type() == QVariant::String) { |
|
283 txt = data.toString(); |
|
284 } else if (data.type() == QVariant::ByteArray) { |
|
285 #ifndef QT_NO_TEXTCODEC |
|
286 QByteArray ba = data.toByteArray(); |
|
287 QTextCodec *codec = Qt::codecForHtml(ba); |
|
288 txt = codec->toUnicode(ba); |
|
289 #else |
|
290 txt = data.toString(); |
|
291 #endif |
|
292 } |
|
293 if (txt.isEmpty()) |
|
294 qWarning("QTextBrowser: No document for %s", url.toString().toLatin1().constData()); |
|
295 |
|
296 if (q->isVisible()) { |
|
297 QString firstTag = txt.left(txt.indexOf(QLatin1Char('>')) + 1); |
|
298 if (firstTag.startsWith(QLatin1String("<qt")) && firstTag.contains(QLatin1String("type")) && firstTag.contains(QLatin1String("detail"))) { |
|
299 #ifndef QT_NO_CURSOR |
|
300 QApplication::restoreOverrideCursor(); |
|
301 #endif |
|
302 #ifndef QT_NO_WHATSTHIS |
|
303 QWhatsThis::showText(QCursor::pos(), txt, q); |
|
304 #endif |
|
305 return; |
|
306 } |
|
307 } |
|
308 |
|
309 currentURL = resolveUrl(url); |
|
310 doSetText = true; |
|
311 } |
|
312 |
|
313 if (!home.isValid()) |
|
314 home = url; |
|
315 |
|
316 if (doSetText) { |
|
317 #ifndef QT_NO_TEXTHTMLPARSER |
|
318 q->QTextEdit::setHtml(txt); |
|
319 q->document()->setMetaInformation(QTextDocument::DocumentUrl, currentURL.toString()); |
|
320 #else |
|
321 q->QTextEdit::setPlainText(txt); |
|
322 #endif |
|
323 |
|
324 #ifdef QT_KEYPAD_NAVIGATION |
|
325 prevFocus.movePosition(QTextCursor::Start); |
|
326 #endif |
|
327 } |
|
328 |
|
329 forceLoadOnSourceChange = false; |
|
330 |
|
331 if (!url.fragment().isEmpty()) { |
|
332 q->scrollToAnchor(url.fragment()); |
|
333 } else { |
|
334 hbar->setValue(0); |
|
335 vbar->setValue(0); |
|
336 } |
|
337 #ifdef QT_KEYPAD_NAVIGATION |
|
338 lastKeypadScrollValue = vbar->value(); |
|
339 emit q->highlighted(QUrl()); |
|
340 emit q->highlighted(QString()); |
|
341 #endif |
|
342 |
|
343 #ifndef QT_NO_CURSOR |
|
344 if (q->isVisible()) |
|
345 QApplication::restoreOverrideCursor(); |
|
346 #endif |
|
347 emit q->sourceChanged(url); |
|
348 } |
|
349 |
|
350 #ifdef QT_KEYPAD_NAVIGATION |
|
351 void QTextBrowserPrivate::keypadMove(bool next) |
|
352 { |
|
353 Q_Q(QTextBrowser); |
|
354 |
|
355 const int height = viewport->height(); |
|
356 const int overlap = qBound(20, height / 5, 40); // XXX arbitrary, but a good balance |
|
357 const int visibleLinkAmount = overlap; // consistent, but maybe not the best choice (?) |
|
358 int yOffset = vbar->value(); |
|
359 int scrollYOffset = qBound(0, next ? yOffset + height - overlap : yOffset - height + overlap, vbar->maximum()); |
|
360 |
|
361 bool foundNextAnchor = false; |
|
362 bool focusIt = false; |
|
363 int focusedPos = -1; |
|
364 |
|
365 QTextCursor anchorToFocus; |
|
366 |
|
367 QRectF viewRect = QRectF(0, yOffset, control->size().width(), height); |
|
368 QRectF newViewRect = QRectF(0, scrollYOffset, control->size().width(), height); |
|
369 QRectF bothViewRects = viewRect.united(newViewRect); |
|
370 |
|
371 // If we don't have a previous anchor, pretend that we had the first/last character |
|
372 // on the screen selected. |
|
373 if (prevFocus.isNull()) { |
|
374 if (next) |
|
375 prevFocus = control->cursorForPosition(QPointF(0, yOffset)); |
|
376 else |
|
377 prevFocus = control->cursorForPosition(QPointF(control->size().width(), yOffset + height)); |
|
378 } |
|
379 |
|
380 // First, check to see if someone has moved the scroll bars independently |
|
381 if (lastKeypadScrollValue != yOffset) { |
|
382 // Someone (user or programmatically) has moved us, so we might |
|
383 // need to start looking from the current position instead of prevFocus |
|
384 |
|
385 bool findOnScreen = true; |
|
386 |
|
387 // If prevFocus is on screen at all, we just use it. |
|
388 if (prevFocus.hasSelection()) { |
|
389 QRectF prevRect = control->selectionRect(prevFocus); |
|
390 if (viewRect.intersects(prevRect)) |
|
391 findOnScreen = false; |
|
392 } |
|
393 |
|
394 // Otherwise, we find a new anchor that's on screen. |
|
395 // Basically, create a cursor with the last/first character |
|
396 // on screen |
|
397 if (findOnScreen) { |
|
398 if (next) |
|
399 prevFocus = control->cursorForPosition(QPointF(0, yOffset)); |
|
400 else |
|
401 prevFocus = control->cursorForPosition(QPointF(control->size().width(), yOffset + height)); |
|
402 } |
|
403 foundNextAnchor = control->findNextPrevAnchor(prevFocus, next, anchorToFocus); |
|
404 } else if (prevFocus.hasSelection()) { |
|
405 // Check the pathological case that the current anchor is higher |
|
406 // than the screen, and just scroll through it in that case |
|
407 QRectF prevRect = control->selectionRect(prevFocus); |
|
408 if ((next && prevRect.bottom() > (yOffset + height)) || |
|
409 (!next && prevRect.top() < yOffset)) { |
|
410 anchorToFocus = prevFocus; |
|
411 focusedPos = scrollYOffset; |
|
412 focusIt = true; |
|
413 } else { |
|
414 // This is the "normal" case - no scroll bar adjustments, no large anchors, |
|
415 // and no wrapping. |
|
416 foundNextAnchor = control->findNextPrevAnchor(prevFocus, next, anchorToFocus); |
|
417 } |
|
418 } |
|
419 |
|
420 // If not found yet, see if we need to wrap |
|
421 if (!focusIt && !foundNextAnchor) { |
|
422 if (next) { |
|
423 if (yOffset == vbar->maximum()) { |
|
424 prevFocus.movePosition(QTextCursor::Start); |
|
425 yOffset = scrollYOffset = 0; |
|
426 |
|
427 // Refresh the rectangles |
|
428 viewRect = QRectF(0, yOffset, control->size().width(), height); |
|
429 newViewRect = QRectF(0, scrollYOffset, control->size().width(), height); |
|
430 bothViewRects = viewRect.united(newViewRect); |
|
431 } |
|
432 } else { |
|
433 if (yOffset == 0) { |
|
434 prevFocus.movePosition(QTextCursor::End); |
|
435 yOffset = scrollYOffset = vbar->maximum(); |
|
436 |
|
437 // Refresh the rectangles |
|
438 viewRect = QRectF(0, yOffset, control->size().width(), height); |
|
439 newViewRect = QRectF(0, scrollYOffset, control->size().width(), height); |
|
440 bothViewRects = viewRect.united(newViewRect); |
|
441 } |
|
442 } |
|
443 |
|
444 // Try looking now |
|
445 foundNextAnchor = control->findNextPrevAnchor(prevFocus, next, anchorToFocus); |
|
446 } |
|
447 |
|
448 // If we did actually find an anchor to use... |
|
449 if (foundNextAnchor) { |
|
450 QRectF desiredRect = control->selectionRect(anchorToFocus); |
|
451 |
|
452 // XXX This is an arbitrary heuristic |
|
453 // Decide to focus an anchor if it will be at least be |
|
454 // in the middle region of the screen after a scroll. |
|
455 // This can result in partial anchors with focus, but |
|
456 // insisting on links being completely visible before |
|
457 // selecting them causes disparities between links that |
|
458 // take up 90% of the screen height and those that take |
|
459 // up e.g. 110% |
|
460 // Obviously if a link is entirely visible, we still |
|
461 // focus it. |
|
462 if(bothViewRects.contains(desiredRect) |
|
463 || bothViewRects.adjusted(0, visibleLinkAmount, 0, -visibleLinkAmount).intersects(desiredRect)) { |
|
464 focusIt = true; |
|
465 |
|
466 // We aim to put the new link in the middle of the screen, |
|
467 // unless the link is larger than the screen (we just move to |
|
468 // display the first page of the link) |
|
469 if (desiredRect.height() > height) { |
|
470 if (next) |
|
471 focusedPos = (int) desiredRect.top(); |
|
472 else |
|
473 focusedPos = (int) desiredRect.bottom() - height; |
|
474 } else |
|
475 focusedPos = (int) ((desiredRect.top() + desiredRect.bottom()) / 2 - (height / 2)); |
|
476 |
|
477 // and clamp it to make sure we don't skip content. |
|
478 if (next) |
|
479 focusedPos = qBound(yOffset, focusedPos, scrollYOffset); |
|
480 else |
|
481 focusedPos = qBound(scrollYOffset, focusedPos, yOffset); |
|
482 } |
|
483 } |
|
484 |
|
485 // If we didn't get a new anchor, check if the old one is still on screen when we scroll |
|
486 // Note that big (larger than screen height) anchors also have some handling at the |
|
487 // start of this function. |
|
488 if (!focusIt && prevFocus.hasSelection()) { |
|
489 QRectF desiredRect = control->selectionRect(prevFocus); |
|
490 // XXX this may be better off also using the visibleLinkAmount value |
|
491 if(newViewRect.intersects(desiredRect)) { |
|
492 focusedPos = scrollYOffset; |
|
493 focusIt = true; |
|
494 anchorToFocus = prevFocus; |
|
495 } |
|
496 } |
|
497 |
|
498 // setTextCursor ensures that the cursor is visible. save & restore |
|
499 // the scroll bar values therefore |
|
500 const int savedXOffset = hbar->value(); |
|
501 |
|
502 // Now actually process our decision |
|
503 if (focusIt && control->setFocusToAnchor(anchorToFocus)) { |
|
504 // Save the focus for next time |
|
505 prevFocus = control->textCursor(); |
|
506 |
|
507 // Scroll |
|
508 vbar->setValue(focusedPos); |
|
509 lastKeypadScrollValue = focusedPos; |
|
510 hbar->setValue(savedXOffset); |
|
511 |
|
512 // Ensure that the new selection is highlighted. |
|
513 const QString href = control->anchorAtCursor(); |
|
514 QUrl url = resolveUrl(href); |
|
515 emit q->highlighted(url); |
|
516 emit q->highlighted(url.toString()); |
|
517 } else { |
|
518 // Scroll |
|
519 vbar->setValue(scrollYOffset); |
|
520 lastKeypadScrollValue = scrollYOffset; |
|
521 |
|
522 // now make sure we don't have a focused anchor |
|
523 QTextCursor cursor = control->textCursor(); |
|
524 cursor.clearSelection(); |
|
525 |
|
526 control->setTextCursor(cursor); |
|
527 |
|
528 hbar->setValue(savedXOffset); |
|
529 vbar->setValue(scrollYOffset); |
|
530 |
|
531 emit q->highlighted(QUrl()); |
|
532 emit q->highlighted(QString()); |
|
533 } |
|
534 } |
|
535 #endif |
|
536 |
|
537 QTextBrowserPrivate::HistoryEntry QTextBrowserPrivate::createHistoryEntry() const |
|
538 { |
|
539 HistoryEntry entry; |
|
540 entry.url = q_func()->source(); |
|
541 entry.title = q_func()->documentTitle(); |
|
542 entry.hpos = hbar->value(); |
|
543 entry.vpos = vbar->value(); |
|
544 |
|
545 const QTextCursor cursor = control->textCursor(); |
|
546 if (control->cursorIsFocusIndicator() |
|
547 && cursor.hasSelection()) { |
|
548 |
|
549 entry.focusIndicatorPosition = cursor.position(); |
|
550 entry.focusIndicatorAnchor = cursor.anchor(); |
|
551 } |
|
552 return entry; |
|
553 } |
|
554 |
|
555 void QTextBrowserPrivate::restoreHistoryEntry(const HistoryEntry entry) |
|
556 { |
|
557 setSource(entry.url); |
|
558 hbar->setValue(entry.hpos); |
|
559 vbar->setValue(entry.vpos); |
|
560 if (entry.focusIndicatorAnchor != -1 && entry.focusIndicatorPosition != -1) { |
|
561 QTextCursor cursor(control->document()); |
|
562 cursor.setPosition(entry.focusIndicatorAnchor); |
|
563 cursor.setPosition(entry.focusIndicatorPosition, QTextCursor::KeepAnchor); |
|
564 control->setTextCursor(cursor); |
|
565 control->setCursorIsFocusIndicator(true); |
|
566 } |
|
567 #ifdef QT_KEYPAD_NAVIGATION |
|
568 lastKeypadScrollValue = vbar->value(); |
|
569 prevFocus = control->textCursor(); |
|
570 |
|
571 Q_Q(QTextBrowser); |
|
572 const QString href = prevFocus.charFormat().anchorHref(); |
|
573 QUrl url = resolveUrl(href); |
|
574 emit q->highlighted(url); |
|
575 emit q->highlighted(url.toString()); |
|
576 #endif |
|
577 } |
|
578 |
|
579 /*! |
|
580 \class QTextBrowser |
|
581 \brief The QTextBrowser class provides a rich text browser with hypertext navigation. |
|
582 |
|
583 \ingroup richtext-processing |
|
584 |
|
585 This class extends QTextEdit (in read-only mode), adding some navigation |
|
586 functionality so that users can follow links in hypertext documents. |
|
587 |
|
588 If you want to provide your users with an editable rich text editor, |
|
589 use QTextEdit. If you want a text browser without hypertext navigation |
|
590 use QTextEdit, and use QTextEdit::setReadOnly() to disable |
|
591 editing. If you just need to display a small piece of rich text |
|
592 use QLabel. |
|
593 |
|
594 \section1 Document Source and Contents |
|
595 |
|
596 The contents of QTextEdit are set with setHtml() or setPlainText(), |
|
597 but QTextBrowser also implements the setSource() function, making it |
|
598 possible to use a named document as the source text. The name is looked |
|
599 up in a list of search paths and in the directory of the current document |
|
600 factory. |
|
601 |
|
602 If a document name ends with |
|
603 an anchor (for example, "\c #anchor"), the text browser automatically |
|
604 scrolls to that position (using scrollToAnchor()). When the user clicks |
|
605 on a hyperlink, the browser will call setSource() itself with the link's |
|
606 \c href value as argument. You can track the current source by connecting |
|
607 to the sourceChanged() signal. |
|
608 |
|
609 \section1 Navigation |
|
610 |
|
611 QTextBrowser provides backward() and forward() slots which you can |
|
612 use to implement Back and Forward buttons. The home() slot sets |
|
613 the text to the very first document displayed. The anchorClicked() |
|
614 signal is emitted when the user clicks an anchor. To override the |
|
615 default navigation behavior of the browser, call the setSource() |
|
616 function to supply new document text in a slot connected to this |
|
617 signal. |
|
618 |
|
619 If you want to load documents stored in the Qt resource system use |
|
620 \c{qrc} as the scheme in the URL to load. For example, for the document |
|
621 resource path \c{:/docs/index.html} use \c{qrc:/docs/index.html} as |
|
622 the URL with setSource(). |
|
623 |
|
624 \sa QTextEdit, QTextDocument |
|
625 */ |
|
626 |
|
627 /*! |
|
628 \property QTextBrowser::modified |
|
629 \brief whether the contents of the text browser have been modified |
|
630 */ |
|
631 |
|
632 /*! |
|
633 \property QTextBrowser::readOnly |
|
634 \brief whether the text browser is read-only |
|
635 |
|
636 By default, this property is true. |
|
637 */ |
|
638 |
|
639 /*! |
|
640 \property QTextBrowser::undoRedoEnabled |
|
641 \brief whether the text browser supports undo/redo operations |
|
642 |
|
643 By default, this property is false. |
|
644 */ |
|
645 |
|
646 void QTextBrowserPrivate::init() |
|
647 { |
|
648 Q_Q(QTextBrowser); |
|
649 control->setTextInteractionFlags(Qt::TextBrowserInteraction); |
|
650 #ifndef QT_NO_CURSOR |
|
651 viewport->setCursor(oldCursor); |
|
652 #endif |
|
653 q->setUndoRedoEnabled(false); |
|
654 viewport->setMouseTracking(true); |
|
655 QObject::connect(q->document(), SIGNAL(contentsChanged()), q, SLOT(_q_documentModified())); |
|
656 QObject::connect(control, SIGNAL(linkActivated(QString)), |
|
657 q, SLOT(_q_activateAnchor(QString))); |
|
658 QObject::connect(control, SIGNAL(linkHovered(QString)), |
|
659 q, SLOT(_q_highlightLink(QString))); |
|
660 } |
|
661 |
|
662 /*! |
|
663 Constructs an empty QTextBrowser with parent \a parent. |
|
664 */ |
|
665 QTextBrowser::QTextBrowser(QWidget *parent) |
|
666 : QTextEdit(*new QTextBrowserPrivate, parent) |
|
667 { |
|
668 Q_D(QTextBrowser); |
|
669 d->init(); |
|
670 } |
|
671 |
|
672 #ifdef QT3_SUPPORT |
|
673 /*! |
|
674 Use one of the constructors that doesn't take the \a name |
|
675 argument and then use setObjectName() instead. |
|
676 */ |
|
677 QTextBrowser::QTextBrowser(QWidget *parent, const char *name) |
|
678 : QTextEdit(*new QTextBrowserPrivate, parent) |
|
679 { |
|
680 setObjectName(QString::fromAscii(name)); |
|
681 Q_D(QTextBrowser); |
|
682 d->init(); |
|
683 } |
|
684 #endif |
|
685 |
|
686 /*! |
|
687 \internal |
|
688 */ |
|
689 QTextBrowser::~QTextBrowser() |
|
690 { |
|
691 } |
|
692 |
|
693 /*! |
|
694 \property QTextBrowser::source |
|
695 \brief the name of the displayed document. |
|
696 |
|
697 This is a an invalid url if no document is displayed or if the |
|
698 source is unknown. |
|
699 |
|
700 When setting this property QTextBrowser tries to find a document |
|
701 with the specified name in the paths of the searchPaths property |
|
702 and directory of the current source, unless the value is an absolute |
|
703 file path. It also checks for optional anchors and scrolls the document |
|
704 accordingly |
|
705 |
|
706 If the first tag in the document is \c{<qt type=detail>}, the |
|
707 document is displayed as a popup rather than as new document in |
|
708 the browser window itself. Otherwise, the document is displayed |
|
709 normally in the text browser with the text set to the contents of |
|
710 the named document with setHtml(). |
|
711 |
|
712 By default, this property contains an empty URL. |
|
713 */ |
|
714 QUrl QTextBrowser::source() const |
|
715 { |
|
716 Q_D(const QTextBrowser); |
|
717 if (d->stack.isEmpty()) |
|
718 return QUrl(); |
|
719 else |
|
720 return d->stack.top().url; |
|
721 } |
|
722 |
|
723 /*! |
|
724 \property QTextBrowser::searchPaths |
|
725 \brief the search paths used by the text browser to find supporting |
|
726 content |
|
727 |
|
728 QTextBrowser uses this list to locate images and documents. |
|
729 |
|
730 By default, this property contains an empty string list. |
|
731 */ |
|
732 |
|
733 QStringList QTextBrowser::searchPaths() const |
|
734 { |
|
735 Q_D(const QTextBrowser); |
|
736 return d->searchPaths; |
|
737 } |
|
738 |
|
739 void QTextBrowser::setSearchPaths(const QStringList &paths) |
|
740 { |
|
741 Q_D(QTextBrowser); |
|
742 d->searchPaths = paths; |
|
743 } |
|
744 |
|
745 /*! |
|
746 Reloads the current set source. |
|
747 */ |
|
748 void QTextBrowser::reload() |
|
749 { |
|
750 Q_D(QTextBrowser); |
|
751 QUrl s = d->currentURL; |
|
752 d->currentURL = QUrl(); |
|
753 setSource(s); |
|
754 } |
|
755 |
|
756 void QTextBrowser::setSource(const QUrl &url) |
|
757 { |
|
758 Q_D(QTextBrowser); |
|
759 |
|
760 const QTextBrowserPrivate::HistoryEntry historyEntry = d->createHistoryEntry(); |
|
761 |
|
762 d->setSource(url); |
|
763 |
|
764 if (!url.isValid()) |
|
765 return; |
|
766 |
|
767 // the same url you are already watching? |
|
768 if (!d->stack.isEmpty() && d->stack.top().url == url) |
|
769 return; |
|
770 |
|
771 if (!d->stack.isEmpty()) |
|
772 d->stack.top() = historyEntry; |
|
773 |
|
774 QTextBrowserPrivate::HistoryEntry entry; |
|
775 entry.url = url; |
|
776 entry.title = documentTitle(); |
|
777 entry.hpos = 0; |
|
778 entry.vpos = 0; |
|
779 d->stack.push(entry); |
|
780 |
|
781 emit backwardAvailable(d->stack.count() > 1); |
|
782 |
|
783 if (!d->forwardStack.isEmpty() && d->forwardStack.top().url == url) { |
|
784 d->forwardStack.pop(); |
|
785 emit forwardAvailable(d->forwardStack.count() > 0); |
|
786 } else { |
|
787 d->forwardStack.clear(); |
|
788 emit forwardAvailable(false); |
|
789 } |
|
790 |
|
791 emit historyChanged(); |
|
792 } |
|
793 |
|
794 /*! |
|
795 \fn void QTextBrowser::backwardAvailable(bool available) |
|
796 |
|
797 This signal is emitted when the availability of backward() |
|
798 changes. \a available is false when the user is at home(); |
|
799 otherwise it is true. |
|
800 */ |
|
801 |
|
802 /*! |
|
803 \fn void QTextBrowser::forwardAvailable(bool available) |
|
804 |
|
805 This signal is emitted when the availability of forward() changes. |
|
806 \a available is true after the user navigates backward() and false |
|
807 when the user navigates or goes forward(). |
|
808 */ |
|
809 |
|
810 /*! |
|
811 \fn void QTextBrowser::historyChanged() |
|
812 \since 4.4 |
|
813 |
|
814 This signal is emitted when the history changes. |
|
815 |
|
816 \sa historyTitle(), historyUrl() |
|
817 */ |
|
818 |
|
819 /*! |
|
820 \fn void QTextBrowser::sourceChanged(const QUrl &src) |
|
821 |
|
822 This signal is emitted when the source has changed, \a src |
|
823 being the new source. |
|
824 |
|
825 Source changes happen both programmatically when calling |
|
826 setSource(), forward(), backword() or home() or when the user |
|
827 clicks on links or presses the equivalent key sequences. |
|
828 */ |
|
829 |
|
830 /*! \fn void QTextBrowser::highlighted(const QUrl &link) |
|
831 |
|
832 This signal is emitted when the user has selected but not |
|
833 activated an anchor in the document. The URL referred to by the |
|
834 anchor is passed in \a link. |
|
835 */ |
|
836 |
|
837 /*! \fn void QTextBrowser::highlighted(const QString &link) |
|
838 \overload |
|
839 |
|
840 Convenience signal that allows connecting to a slot |
|
841 that takes just a QString, like for example QStatusBar's |
|
842 message(). |
|
843 */ |
|
844 |
|
845 |
|
846 /*! |
|
847 \fn void QTextBrowser::anchorClicked(const QUrl &link) |
|
848 |
|
849 This signal is emitted when the user clicks an anchor. The |
|
850 URL referred to by the anchor is passed in \a link. |
|
851 |
|
852 Note that the browser will automatically handle navigation to the |
|
853 location specified by \a link unless the openLinks property |
|
854 is set to false or you call setSource() in a slot connected. |
|
855 This mechanism is used to override the default navigation features of the browser. |
|
856 */ |
|
857 |
|
858 /*! |
|
859 Changes the document displayed to the previous document in the |
|
860 list of documents built by navigating links. Does nothing if there |
|
861 is no previous document. |
|
862 |
|
863 \sa forward(), backwardAvailable() |
|
864 */ |
|
865 void QTextBrowser::backward() |
|
866 { |
|
867 Q_D(QTextBrowser); |
|
868 if (d->stack.count() <= 1) |
|
869 return; |
|
870 |
|
871 // Update the history entry |
|
872 d->forwardStack.push(d->createHistoryEntry()); |
|
873 d->stack.pop(); // throw away the old version of the current entry |
|
874 d->restoreHistoryEntry(d->stack.top()); // previous entry |
|
875 emit backwardAvailable(d->stack.count() > 1); |
|
876 emit forwardAvailable(true); |
|
877 emit historyChanged(); |
|
878 } |
|
879 |
|
880 /*! |
|
881 Changes the document displayed to the next document in the list of |
|
882 documents built by navigating links. Does nothing if there is no |
|
883 next document. |
|
884 |
|
885 \sa backward(), forwardAvailable() |
|
886 */ |
|
887 void QTextBrowser::forward() |
|
888 { |
|
889 Q_D(QTextBrowser); |
|
890 if (d->forwardStack.isEmpty()) |
|
891 return; |
|
892 if (!d->stack.isEmpty()) { |
|
893 // Update the history entry |
|
894 d->stack.top() = d->createHistoryEntry(); |
|
895 } |
|
896 d->stack.push(d->forwardStack.pop()); |
|
897 d->restoreHistoryEntry(d->stack.top()); |
|
898 emit backwardAvailable(true); |
|
899 emit forwardAvailable(!d->forwardStack.isEmpty()); |
|
900 emit historyChanged(); |
|
901 } |
|
902 |
|
903 /*! |
|
904 Changes the document displayed to be the first document from |
|
905 the history. |
|
906 */ |
|
907 void QTextBrowser::home() |
|
908 { |
|
909 Q_D(QTextBrowser); |
|
910 if (d->home.isValid()) |
|
911 setSource(d->home); |
|
912 } |
|
913 |
|
914 /*! |
|
915 The event \a ev is used to provide the following keyboard shortcuts: |
|
916 \table |
|
917 \header \i Keypress \i Action |
|
918 \row \i Alt+Left Arrow \i \l backward() |
|
919 \row \i Alt+Right Arrow \i \l forward() |
|
920 \row \i Alt+Up Arrow \i \l home() |
|
921 \endtable |
|
922 */ |
|
923 void QTextBrowser::keyPressEvent(QKeyEvent *ev) |
|
924 { |
|
925 #ifdef QT_KEYPAD_NAVIGATION |
|
926 Q_D(QTextBrowser); |
|
927 switch (ev->key()) { |
|
928 case Qt::Key_Select: |
|
929 if (QApplication::keypadNavigationEnabled()) { |
|
930 if (!hasEditFocus()) { |
|
931 setEditFocus(true); |
|
932 return; |
|
933 } else { |
|
934 QTextCursor cursor = d->control->textCursor(); |
|
935 QTextCharFormat charFmt = cursor.charFormat(); |
|
936 if (!cursor.hasSelection() || charFmt.anchorHref().isEmpty()) { |
|
937 ev->accept(); |
|
938 return; |
|
939 } |
|
940 } |
|
941 } |
|
942 break; |
|
943 case Qt::Key_Back: |
|
944 if (QApplication::keypadNavigationEnabled()) { |
|
945 if (hasEditFocus()) { |
|
946 setEditFocus(false); |
|
947 ev->accept(); |
|
948 return; |
|
949 } |
|
950 } |
|
951 QTextEdit::keyPressEvent(ev); |
|
952 return; |
|
953 default: |
|
954 if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) { |
|
955 ev->ignore(); |
|
956 return; |
|
957 } |
|
958 } |
|
959 #endif |
|
960 |
|
961 if (ev->modifiers() & Qt::AltModifier) { |
|
962 switch (ev->key()) { |
|
963 case Qt::Key_Right: |
|
964 forward(); |
|
965 ev->accept(); |
|
966 return; |
|
967 case Qt::Key_Left: |
|
968 backward(); |
|
969 ev->accept(); |
|
970 return; |
|
971 case Qt::Key_Up: |
|
972 home(); |
|
973 ev->accept(); |
|
974 return; |
|
975 } |
|
976 } |
|
977 #ifdef QT_KEYPAD_NAVIGATION |
|
978 else { |
|
979 if (ev->key() == Qt::Key_Up) { |
|
980 d->keypadMove(false); |
|
981 return; |
|
982 } else if (ev->key() == Qt::Key_Down) { |
|
983 d->keypadMove(true); |
|
984 return; |
|
985 } |
|
986 } |
|
987 #endif |
|
988 QTextEdit::keyPressEvent(ev); |
|
989 } |
|
990 |
|
991 /*! |
|
992 \reimp |
|
993 */ |
|
994 void QTextBrowser::mouseMoveEvent(QMouseEvent *e) |
|
995 { |
|
996 QTextEdit::mouseMoveEvent(e); |
|
997 } |
|
998 |
|
999 /*! |
|
1000 \reimp |
|
1001 */ |
|
1002 void QTextBrowser::mousePressEvent(QMouseEvent *e) |
|
1003 { |
|
1004 QTextEdit::mousePressEvent(e); |
|
1005 } |
|
1006 |
|
1007 /*! |
|
1008 \reimp |
|
1009 */ |
|
1010 void QTextBrowser::mouseReleaseEvent(QMouseEvent *e) |
|
1011 { |
|
1012 QTextEdit::mouseReleaseEvent(e); |
|
1013 } |
|
1014 |
|
1015 /*! |
|
1016 \reimp |
|
1017 */ |
|
1018 void QTextBrowser::focusOutEvent(QFocusEvent *ev) |
|
1019 { |
|
1020 #ifndef QT_NO_CURSOR |
|
1021 Q_D(QTextBrowser); |
|
1022 d->viewport->setCursor((!(d->control->textInteractionFlags() & Qt::TextEditable)) ? d->oldCursor : Qt::IBeamCursor); |
|
1023 #endif |
|
1024 QTextEdit::focusOutEvent(ev); |
|
1025 } |
|
1026 |
|
1027 /*! |
|
1028 \reimp |
|
1029 */ |
|
1030 bool QTextBrowser::focusNextPrevChild(bool next) |
|
1031 { |
|
1032 Q_D(QTextBrowser); |
|
1033 if (d->control->setFocusToNextOrPreviousAnchor(next)) { |
|
1034 #ifdef QT_KEYPAD_NAVIGATION |
|
1035 // Might need to synthesize a highlight event. |
|
1036 if (d->prevFocus != d->control->textCursor() && d->control->textCursor().hasSelection()) { |
|
1037 const QString href = d->control->anchorAtCursor(); |
|
1038 QUrl url = d->resolveUrl(href); |
|
1039 emit highlighted(url); |
|
1040 emit highlighted(url.toString()); |
|
1041 } |
|
1042 d->prevFocus = d->control->textCursor(); |
|
1043 #endif |
|
1044 return true; |
|
1045 } else { |
|
1046 #ifdef QT_KEYPAD_NAVIGATION |
|
1047 // We assume we have no highlight now. |
|
1048 emit highlighted(QUrl()); |
|
1049 emit highlighted(QString()); |
|
1050 #endif |
|
1051 } |
|
1052 return QTextEdit::focusNextPrevChild(next); |
|
1053 } |
|
1054 |
|
1055 /*! |
|
1056 \reimp |
|
1057 */ |
|
1058 void QTextBrowser::paintEvent(QPaintEvent *e) |
|
1059 { |
|
1060 Q_D(QTextBrowser); |
|
1061 QPainter p(d->viewport); |
|
1062 d->paint(&p, e); |
|
1063 } |
|
1064 |
|
1065 /*! |
|
1066 This function is called when the document is loaded and for |
|
1067 each image in the document. The \a type indicates the type of resource |
|
1068 to be loaded. An invalid QVariant is returned if the resource cannot be |
|
1069 loaded. |
|
1070 |
|
1071 The default implementation ignores \a type and tries to locate |
|
1072 the resources by interpreting \a name as a file name. If it is |
|
1073 not an absolute path it tries to find the file in the paths of |
|
1074 the \l searchPaths property and in the same directory as the |
|
1075 current source. On success, the result is a QVariant that stores |
|
1076 a QByteArray with the contents of the file. |
|
1077 |
|
1078 If you reimplement this function, you can return other QVariant |
|
1079 types. The table below shows which variant types are supported |
|
1080 depending on the resource type: |
|
1081 |
|
1082 \table |
|
1083 \header \i ResourceType \i QVariant::Type |
|
1084 \row \i QTextDocument::HtmlResource \i QString or QByteArray |
|
1085 \row \i QTextDocument::ImageResource \i QImage, QPixmap or QByteArray |
|
1086 \row \i QTextDocument::StyleSheetResource \i QString or QByteArray |
|
1087 \endtable |
|
1088 */ |
|
1089 QVariant QTextBrowser::loadResource(int /*type*/, const QUrl &name) |
|
1090 { |
|
1091 Q_D(QTextBrowser); |
|
1092 |
|
1093 QByteArray data; |
|
1094 QString fileName = d->findFile(d->resolveUrl(name)); |
|
1095 QFile f(fileName); |
|
1096 if (f.open(QFile::ReadOnly)) { |
|
1097 data = f.readAll(); |
|
1098 f.close(); |
|
1099 } else { |
|
1100 return QVariant(); |
|
1101 } |
|
1102 |
|
1103 return data; |
|
1104 } |
|
1105 |
|
1106 /*! |
|
1107 \since 4.2 |
|
1108 |
|
1109 Returns true if the text browser can go backward in the document history |
|
1110 using backward(). |
|
1111 |
|
1112 \sa backwardAvailable(), backward() |
|
1113 */ |
|
1114 bool QTextBrowser::isBackwardAvailable() const |
|
1115 { |
|
1116 Q_D(const QTextBrowser); |
|
1117 return d->stack.count() > 1; |
|
1118 } |
|
1119 |
|
1120 /*! |
|
1121 \since 4.2 |
|
1122 |
|
1123 Returns true if the text browser can go forward in the document history |
|
1124 using forward(). |
|
1125 |
|
1126 \sa forwardAvailable(), forward() |
|
1127 */ |
|
1128 bool QTextBrowser::isForwardAvailable() const |
|
1129 { |
|
1130 Q_D(const QTextBrowser); |
|
1131 return !d->forwardStack.isEmpty(); |
|
1132 } |
|
1133 |
|
1134 /*! |
|
1135 \since 4.2 |
|
1136 |
|
1137 Clears the history of visited documents and disables the forward and |
|
1138 backward navigation. |
|
1139 |
|
1140 \sa backward(), forward() |
|
1141 */ |
|
1142 void QTextBrowser::clearHistory() |
|
1143 { |
|
1144 Q_D(QTextBrowser); |
|
1145 d->forwardStack.clear(); |
|
1146 if (!d->stack.isEmpty()) { |
|
1147 QTextBrowserPrivate::HistoryEntry historyEntry = d->stack.top(); |
|
1148 d->stack.resize(0); |
|
1149 d->stack.push(historyEntry); |
|
1150 d->home = historyEntry.url; |
|
1151 } |
|
1152 emit forwardAvailable(false); |
|
1153 emit backwardAvailable(false); |
|
1154 emit historyChanged(); |
|
1155 } |
|
1156 |
|
1157 /*! |
|
1158 Returns the url of the HistoryItem. |
|
1159 |
|
1160 \table |
|
1161 \header \i Input \i Return |
|
1162 \row \i \a{i} < 0 \i \l backward() history |
|
1163 \row \i\a{i} == 0 \i current, see QTextBrowser::source() |
|
1164 \row \i \a{i} > 0 \i \l forward() history |
|
1165 \endtable |
|
1166 |
|
1167 \since 4.4 |
|
1168 */ |
|
1169 QUrl QTextBrowser::historyUrl(int i) const |
|
1170 { |
|
1171 Q_D(const QTextBrowser); |
|
1172 return d->history(i).url; |
|
1173 } |
|
1174 |
|
1175 /*! |
|
1176 Returns the documentTitle() of the HistoryItem. |
|
1177 |
|
1178 \table |
|
1179 \header \i Input \i Return |
|
1180 \row \i \a{i} < 0 \i \l backward() history |
|
1181 \row \i \a{i} == 0 \i current, see QTextBrowser::source() |
|
1182 \row \i \a{i} > 0 \i \l forward() history |
|
1183 \endtable |
|
1184 |
|
1185 \snippet doc/src/snippets/code/src_gui_widgets_qtextbrowser.cpp 0 |
|
1186 |
|
1187 \since 4.4 |
|
1188 */ |
|
1189 QString QTextBrowser::historyTitle(int i) const |
|
1190 { |
|
1191 Q_D(const QTextBrowser); |
|
1192 return d->history(i).title; |
|
1193 } |
|
1194 |
|
1195 |
|
1196 /*! |
|
1197 Returns the number of locations forward in the history. |
|
1198 |
|
1199 \since 4.4 |
|
1200 */ |
|
1201 int QTextBrowser::forwardHistoryCount() const |
|
1202 { |
|
1203 Q_D(const QTextBrowser); |
|
1204 return d->forwardStack.count(); |
|
1205 } |
|
1206 |
|
1207 /*! |
|
1208 Returns the number of locations backward in the history. |
|
1209 |
|
1210 \since 4.4 |
|
1211 */ |
|
1212 int QTextBrowser::backwardHistoryCount() const |
|
1213 { |
|
1214 Q_D(const QTextBrowser); |
|
1215 return d->stack.count()-1; |
|
1216 } |
|
1217 |
|
1218 /*! |
|
1219 \property QTextBrowser::openExternalLinks |
|
1220 \since 4.2 |
|
1221 |
|
1222 Specifies whether QTextBrowser should automatically open links to external |
|
1223 sources using QDesktopServices::openUrl() instead of emitting the |
|
1224 anchorClicked signal. Links are considered external if their scheme is |
|
1225 neither file or qrc. |
|
1226 |
|
1227 The default value is false. |
|
1228 */ |
|
1229 bool QTextBrowser::openExternalLinks() const |
|
1230 { |
|
1231 Q_D(const QTextBrowser); |
|
1232 return d->openExternalLinks; |
|
1233 } |
|
1234 |
|
1235 void QTextBrowser::setOpenExternalLinks(bool open) |
|
1236 { |
|
1237 Q_D(QTextBrowser); |
|
1238 d->openExternalLinks = open; |
|
1239 } |
|
1240 |
|
1241 /*! |
|
1242 \property QTextBrowser::openLinks |
|
1243 \since 4.3 |
|
1244 |
|
1245 This property specifies whether QTextBrowser should automatically open links the user tries to |
|
1246 activate by mouse or keyboard. |
|
1247 |
|
1248 Regardless of the value of this property the anchorClicked signal is always emitted. |
|
1249 |
|
1250 The default value is true. |
|
1251 */ |
|
1252 |
|
1253 bool QTextBrowser::openLinks() const |
|
1254 { |
|
1255 Q_D(const QTextBrowser); |
|
1256 return d->openLinks; |
|
1257 } |
|
1258 |
|
1259 void QTextBrowser::setOpenLinks(bool open) |
|
1260 { |
|
1261 Q_D(QTextBrowser); |
|
1262 d->openLinks = open; |
|
1263 } |
|
1264 |
|
1265 /*! \reimp */ |
|
1266 bool QTextBrowser::event(QEvent *e) |
|
1267 { |
|
1268 return QTextEdit::event(e); |
|
1269 } |
|
1270 |
|
1271 QT_END_NAMESPACE |
|
1272 |
|
1273 #include "moc_qtextbrowser.cpp" |
|
1274 |
|
1275 #endif // QT_NO_TEXTBROWSER |