source: vendor/trolltech/current/src/widgets/qtextbrowser.cpp

Last change on this file was 2, checked in by dmik, 20 years ago

Imported xplatform parts of the official release 3.3.1 from Trolltech

  • Property svn:keywords set to Id
File size: 15.3 KB
Line 
1/****************************************************************************
2** $Id: qtextbrowser.cpp 2 2005-11-16 15:49:26Z dmik $
3**
4** Implementation of the QTextBrowser class
5**
6** Created : 990101
7**
8** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
9**
10** This file is part of the widgets module of the Qt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.QPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
22** licenses may use this file in accordance with the Qt Commercial License
23** Agreement provided with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about Qt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for QPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38#include "qtextbrowser.h"
39#ifndef QT_NO_TEXTBROWSER
40#include "../kernel/qrichtext_p.h"
41
42#include "qapplication.h"
43#include "qlayout.h"
44#include "qpainter.h"
45
46#include "qvaluestack.h"
47#include "stdio.h"
48#include "qfile.h"
49#include "qtextstream.h"
50#include "qlayout.h"
51#include "qbitmap.h"
52#include "qtimer.h"
53#include "qimage.h"
54#include "qsimplerichtext.h"
55#include "qdragobject.h"
56#include "qurl.h"
57#include "qcursor.h"
58
59/*!
60 \class QTextBrowser qtextbrowser.h
61 \brief The QTextBrowser class provides a rich text browser with hypertext navigation.
62
63 \ingroup advanced
64 \ingroup helpsystem
65 \ingroup text
66 \mainclass
67
68 This class extends QTextEdit (in read-only mode), adding some
69 navigation functionality so that users can follow links in
70 hypertext documents. The contents of QTextEdit is set with
71 setText(), but QTextBrowser has an additional function,
72 setSource(), which makes it possible to set the text to a named
73 document. The name is looked up in the text view's mime source
74 factory. If a document name ends with an anchor (for example, "\c
75 #anchor"), the text browser automatically scrolls to that position
76 (using scrollToAnchor()). When the user clicks on a hyperlink, the
77 browser will call setSource() itself, with the link's \c href
78 value as argument. You can track the current source by connetion
79 to the sourceChanged() signal.
80
81 QTextBrowser provides backward() and forward() slots which you can
82 use to implement Back and Forward buttons. The home() slot sets
83 the text to the very first document displayed. The linkClicked()
84 signal is emitted when the user clicks a link.
85
86 By using QTextEdit::setMimeSourceFactory() you can provide your
87 own subclass of QMimeSourceFactory. This makes it possible to
88 access data from anywhere, for example from a network or from a
89 database. See QMimeSourceFactory::data() for details.
90
91 If you intend using the mime factory to read the data directly
92 from the file system, you may have to specify the encoding for the
93 file extension you are using. For example:
94 \code
95 mimeSourceFactory()->setExtensionType("qml", "text/utf8");
96 \endcode
97 This is to ensure that the factory is able to resolve the document
98 names.
99
100 QTextBrowser interprets the tags it processes in accordance with
101 the default style sheet. Change the style sheet with
102 \l{setStyleSheet()}; see QStyleSheet for details.
103
104 If you want to provide your users with editable rich text use
105 QTextEdit. If you want a text browser without hypertext navigation
106 use QTextEdit, and use QTextEdit::setReadOnly() to disable
107 editing. If you just need to display a small piece of rich text
108 use QSimpleRichText or QLabel.
109
110 <img src=qtextbrowser-m.png> <img src=qtextbrowser-w.png>
111*/
112
113class QTextBrowserData
114{
115public:
116 QTextBrowserData():textOrSourceChanged(FALSE) {}
117
118 QValueStack<QString> stack;
119 QValueStack<QString> forwardStack;
120 QString home;
121 QString curmain;
122 QString curmark;
123
124 /*flag necessary to give the linkClicked() signal some meaningful
125 semantics when somebody connected to it calls setText() or
126 setSource() */
127 bool textOrSourceChanged;
128};
129
130
131/*!
132 Constructs an empty QTextBrowser called \a name, with parent \a
133 parent.
134*/
135QTextBrowser::QTextBrowser(QWidget *parent, const char *name)
136 : QTextEdit( parent, name )
137{
138 setReadOnly( TRUE );
139 d = new QTextBrowserData;
140
141 viewport()->setMouseTracking( TRUE );
142}
143
144/*!
145 \reimp
146*/
147QTextBrowser::~QTextBrowser()
148{
149 delete d;
150}
151
152
153/*!
154 \property QTextBrowser::source
155 \brief the name of the displayed document.
156
157 This is a QString::null if no document is displayed or if the
158 source is unknown.
159
160 Setting this property uses the mimeSourceFactory() to lookup the
161 named document. It also checks for optional anchors and scrolls
162 the document accordingly.
163
164 If the first tag in the document is \c{<qt type=detail>}, the
165 document is displayed as a popup rather than as new document in
166 the browser window itself. Otherwise, the document is displayed
167 normally in the text browser with the text set to the contents of
168 the named document with setText().
169
170 If you are using the filesystem access capabilities of the mime
171 source factory, you must ensure that the factory knows about the
172 encoding of specified files; otherwise no data will be available.
173 The default factory handles a couple of common file extensions
174 such as \c *.html and \c *.txt with reasonable defaults. See
175 QMimeSourceFactory::data() for details.
176*/
177
178QString QTextBrowser::source() const
179{
180 if ( d->stack.isEmpty() )
181 return QString::null;
182 else
183 return d->stack.top();
184}
185
186/*!
187 \property QTextBrowser::undoDepth
188 \brief This text browser's undo depth.
189*/
190
191/*!
192 \property QTextBrowser::overwriteMode
193 \brief This text browser's overwrite mode.
194*/
195
196/*!
197 \property QTextBrowser::modified
198 \brief Whether the contents have been modified.
199*/
200
201/*!
202 \property QTextBrowser::readOnly
203 \brief Whether the contents are read only.
204*/
205
206/*!
207 \property QTextBrowser::undoRedoEnabled
208 \brief Whether undo and redo are enabled.
209*/
210
211
212
213/*!
214 Reloads the current set source.
215*/
216
217void QTextBrowser::reload()
218{
219 QString s = d->curmain;
220 d->curmain = "";
221 setSource( s );
222}
223
224
225void QTextBrowser::setSource(const QString& name)
226{
227#ifndef QT_NO_CURSOR
228 if ( isVisible() )
229 qApp->setOverrideCursor( waitCursor );
230#endif
231 d->textOrSourceChanged = TRUE;
232 QString source = name;
233 QString mark;
234 int hash = name.find('#');
235 if ( hash != -1) {
236 source = name.left( hash );
237 mark = name.mid( hash+1 );
238 }
239
240 if ( source.left(5) == "file:" )
241 source = source.mid(6);
242
243 QString url = mimeSourceFactory()->makeAbsolute( source, context() );
244 QString txt;
245 bool dosettext = FALSE;
246
247 if ( !source.isEmpty() && url != d->curmain ) {
248 const QMimeSource* m =
249 mimeSourceFactory()->data( source, context() );
250 if ( !m ){
251 qWarning("QTextBrowser: no mimesource for %s", source.latin1() );
252 }
253 else {
254 if ( !QTextDrag::decode( m, txt ) ) {
255 qWarning("QTextBrowser: cannot decode %s", source.latin1() );
256 }
257 }
258 if ( isVisible() ) {
259 QString firstTag = txt.left( txt.find( '>' ) + 1 );
260 if ( firstTag.left( 3 ) == "<qt" && firstTag.contains( "type" ) && firstTag.contains( "detail" ) ) {
261 popupDetail( txt, QCursor::pos() );
262#ifndef QT_NO_CURSOR
263 qApp->restoreOverrideCursor();
264#endif
265 return;
266 }
267 }
268
269 d->curmain = url;
270 dosettext = TRUE;
271 }
272
273 d->curmark = mark;
274
275 if ( !mark.isEmpty() ) {
276 url += "#";
277 url += mark;
278 }
279 if ( !d->home )
280 d->home = url;
281
282 if ( d->stack.isEmpty() || d->stack.top() != url)
283 d->stack.push( url );
284
285 int stackCount = (int)d->stack.count();
286 if ( d->stack.top() == url )
287 stackCount--;
288 emit backwardAvailable( stackCount > 0 );
289 stackCount = (int)d->forwardStack.count();
290 if ( d->forwardStack.isEmpty() || d->forwardStack.top() == url )
291 stackCount--;
292 emit forwardAvailable( stackCount > 0 );
293
294 if ( dosettext )
295 QTextEdit::setText( txt, url );
296
297 if ( !mark.isEmpty() )
298 scrollToAnchor( mark );
299 else
300 setContentsPos( 0, 0 );
301
302#ifndef QT_NO_CURSOR
303 if ( isVisible() )
304 qApp->restoreOverrideCursor();
305#endif
306
307 emit sourceChanged( url );
308}
309
310/*!
311 \fn void QTextBrowser::backwardAvailable(bool available)
312
313 This signal is emitted when the availability of backward()
314 changes. \a available is FALSE when the user is at home();
315 otherwise it is TRUE.
316*/
317
318/*!
319 \fn void QTextBrowser::forwardAvailable(bool available)
320
321 This signal is emitted when the availability of forward() changes.
322 \a available is TRUE after the user navigates backward() and FALSE
323 when the user navigates or goes forward().
324*/
325
326/*!
327 \fn void QTextBrowser::sourceChanged( const QString& src)
328
329 This signal is emitted when the mime source has changed, \a src
330 being the new source.
331
332 Source changes happen both programmatically when calling
333 setSource(), forward(), backword() or home() or when the user
334 clicks on links or presses the equivalent key sequences.
335*/
336
337/*! \fn void QTextBrowser::highlighted (const QString &link)
338
339 This signal is emitted when the user has selected but not
340 activated a link in the document. \a link is the value of the \c
341 href i.e. the name of the target document.
342*/
343
344/*!
345 \fn void QTextBrowser::linkClicked( const QString& link)
346
347 This signal is emitted when the user clicks a link. The \a link is
348 the value of the \c href i.e. the name of the target document.
349
350 The \a link will be the absolute location of the document, based
351 on the value of the anchor's href tag and the current context of
352 the document.
353
354 \sa anchorClicked(), context()
355*/
356
357/*!
358 \fn void QTextBrowser::anchorClicked( const QString& name, const QString &link)
359
360 This signal is emitted when the user clicks an anchor. The \a link is
361 the value of the \c href i.e. the name of the target document. The \a name
362 is the name of the anchor.
363
364 \sa linkClicked()
365*/
366
367/*!
368 Changes the document displayed to the previous document in the
369 list of documents built by navigating links. Does nothing if there
370 is no previous document.
371
372 \sa forward(), backwardAvailable()
373*/
374void QTextBrowser::backward()
375{
376 if ( d->stack.count() <= 1)
377 return;
378 d->forwardStack.push( d->stack.pop() );
379 setSource( d->stack.pop() );
380 emit forwardAvailable( TRUE );
381}
382
383/*!
384 Changes the document displayed to the next document in the list of
385 documents built by navigating links. Does nothing if there is no
386 next document.
387
388 \sa backward(), forwardAvailable()
389*/
390void QTextBrowser::forward()
391{
392 if ( d->forwardStack.isEmpty() )
393 return;
394 setSource( d->forwardStack.pop() );
395 emit forwardAvailable( !d->forwardStack.isEmpty() );
396}
397
398/*!
399 Changes the document displayed to be the first document the
400 browser displayed.
401*/
402void QTextBrowser::home()
403{
404 if (!d->home.isNull() )
405 setSource( d->home );
406}
407
408/*!
409 The event \a e is used to provide the following keyboard shortcuts:
410 \table
411 \header \i Keypress \i Action
412 \row \i Alt+Left Arrow \i \l backward()
413 \row \i Alt+Right Arrow \i \l forward()
414 \row \i Alt+Up Arrow \i \l home()
415 \endtable
416*/
417void QTextBrowser::keyPressEvent( QKeyEvent * e )
418{
419 if ( e->state() & AltButton ) {
420 switch (e->key()) {
421 case Key_Right:
422 forward();
423 return;
424 case Key_Left:
425 backward();
426 return;
427 case Key_Up:
428 home();
429 return;
430 }
431 }
432 QTextEdit::keyPressEvent(e);
433}
434
435class QTextDetailPopup : public QWidget
436{
437public:
438 QTextDetailPopup()
439 : QWidget ( 0, "automatic QText detail widget", WType_Popup | WDestructiveClose )
440 {
441 }
442
443protected:
444
445 void mousePressEvent( QMouseEvent*)
446 {
447 close();
448 }
449};
450
451
452void QTextBrowser::popupDetail( const QString& contents, const QPoint& pos )
453{
454
455 const int shadowWidth = 6; // also used as '5' and '6' and even '8' below
456 const int vMargin = 8;
457 const int hMargin = 12;
458
459 QWidget* popup = new QTextDetailPopup;
460 popup->setBackgroundMode( QWidget::NoBackground );
461
462 QSimpleRichText* doc = new QSimpleRichText( contents, popup->font() );
463 doc->adjustSize();
464 QRect r( 0, 0, doc->width(), doc->height() );
465
466 int w = r.width() + 2*hMargin;
467 int h = r.height() + 2*vMargin;
468
469 popup->resize( w + shadowWidth, h + shadowWidth );
470
471 // okay, now to find a suitable location
472 //###### we need a global fancy popup positioning somewhere
473 popup->move(pos - popup->rect().center());
474 if (popup->geometry().right() > QApplication::desktop()->width())
475 popup->move( QApplication::desktop()->width() - popup->width(),
476 popup->y() );
477 if (popup->geometry().bottom() > QApplication::desktop()->height())
478 popup->move( popup->x(),
479 QApplication::desktop()->height() - popup->height() );
480 if ( popup->x() < 0 )
481 popup->move( 0, popup->y() );
482 if ( popup->y() < 0 )
483 popup->move( popup->x(), 0 );
484
485
486 popup->show();
487
488 // now for super-clever shadow stuff. super-clever mostly in
489 // how many window system problems it skirts around.
490
491 QPainter p( popup );
492 p.setPen( QApplication::palette().active().foreground() );
493 p.drawRect( 0, 0, w, h );
494 p.setPen( QApplication::palette().active().mid() );
495 p.setBrush( QColor( 255, 255, 240 ) );
496 p.drawRect( 1, 1, w-2, h-2 );
497 p.setPen( black );
498
499 doc->draw( &p, hMargin, vMargin, r, popup->colorGroup(), 0 );
500 delete doc;
501
502 p.drawPoint( w + 5, 6 );
503 p.drawLine( w + 3, 6,
504 w + 5, 8 );
505 p.drawLine( w + 1, 6,
506 w + 5, 10 );
507 int i;
508 for( i=7; i < h; i += 2 )
509 p.drawLine( w, i,
510 w + 5, i + 5 );
511 for( i = w - i + h; i > 6; i -= 2 )
512 p.drawLine( i, h,
513 i + 5, h + 5 );
514 for( ; i > 0 ; i -= 2 )
515 p.drawLine( 6, h + 6 - i,
516 i + 5, h + 5 );
517}
518
519/*!
520 \fn void QTextBrowser::setText( const QString &txt )
521
522 \overload
523
524 Sets the text to \a txt.
525*/
526
527/*!
528 \reimp
529*/
530
531void QTextBrowser::setText( const QString &txt, const QString &context )
532{
533 d->textOrSourceChanged = TRUE;
534 d->curmark = "";
535 d->curmain = "";
536 QTextEdit::setText( txt, context );
537}
538
539void QTextBrowser::emitHighlighted( const QString &s )
540{
541 emit highlighted( s );
542}
543
544void QTextBrowser::emitLinkClicked( const QString &s )
545{
546 d->textOrSourceChanged = FALSE;
547 emit linkClicked( s );
548 if ( !d->textOrSourceChanged )
549 setSource( s );
550}
551
552#endif // QT_NO_TEXTBROWSER
Note: See TracBrowser for help on using the repository browser.