source: vendor/trolltech/current/src/dialogs/qprogressdialog.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: 21.9 KB
Line 
1/****************************************************************************
2** $Id: qprogressdialog.cpp 2 2005-11-16 15:49:26Z dmik $
3**
4** Implementation of QProgressDialog class
5**
6** Created : 970521
7**
8** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
9**
10** This file is part of the dialogs 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 "qprogressdialog.h"
39
40#ifndef QT_NO_PROGRESSDIALOG
41
42#include "qaccel.h"
43#include "qpainter.h"
44#include "qdrawutil.h"
45#include "qdatetime.h"
46#include "qapplication.h"
47#include "qstyle.h"
48#include "qpushbutton.h"
49#include "qcursor.h"
50#include "qtimer.h"
51#include <limits.h>
52
53// If the operation is expected to take this long (as predicted by
54// progress time), show the progress dialog.
55static const int defaultShowTime = 4000;
56// Wait at least this long before attempting to make a prediction.
57static const int minWaitTime = 50;
58
59// Various layout values
60static const int margin_lr = 10;
61static const int margin_tb = 10;
62static const int spacing = 4;
63
64
65class QProgressDialogData
66{
67public:
68 QProgressDialogData( QProgressDialog* that, QWidget* parent,
69 const QString& labelText,
70 int totalSteps ) :
71 creator( parent ),
72 label( new QLabel(labelText,that,"label") ),
73 cancel( 0 ),
74 bar( new QProgressBar(totalSteps,that,"bar") ),
75 shown_once( FALSE ),
76 cancellation_flag( FALSE ),
77 showTime( defaultShowTime )
78 {
79 label->setAlignment(that->style().styleHint(QStyle::SH_ProgressDialog_TextLabelAlignment, that));
80 }
81
82 QWidget *creator;
83 QLabel *label;
84 QPushButton *cancel;
85 QProgressBar *bar;
86 bool shown_once;
87 bool cancellation_flag;
88 QTime starttime;
89#ifndef QT_NO_CURSOR
90 QCursor parentCursor;
91#endif
92 int showTime;
93 bool autoClose;
94 bool autoReset;
95 bool forceHide;
96};
97
98
99/*!
100 \class QProgressDialog qprogressdialog.h
101 \brief The QProgressDialog class provides feedback on the progress of a slow operation.
102 \ingroup dialogs
103 \mainclass
104
105 A progress dialog is used to give the user an indication of how long
106 an operation is going to take, and to demonstrate that the
107 application has not frozen. It can also give the user an opportunity
108 to abort the operation.
109
110 A common problem with progress dialogs is that it is difficult to know
111 when to use them; operations take different amounts of time on different
112 hardware. QProgressDialog offers a solution to this problem:
113 it estimates the time the operation will take (based on time for
114 steps), and only shows itself if that estimate is beyond minimumDuration()
115 (4 seconds by default).
116
117 Use setTotalSteps() (or the constructor) to set the number of
118 "steps" in the operation and call setProgress() as the operation
119 progresses. The step value can be chosen arbitrarily. It can be the
120 number of files copied, the number of bytes received, the number of
121 iterations through the main loop of your algorithm, or some other
122 suitable unit. Progress starts at 0, and the progress dialog shows
123 that the operation has finished when you call setProgress() with
124 totalSteps() as its argument.
125
126 The dialog automatically resets and hides itself at the end of the
127 operation. Use setAutoReset() and setAutoClose() to change this
128 behavior.
129
130 There are two ways of using QProgressDialog: modal and modeless.
131
132 Using a modal QProgressDialog is simpler for the programmer, but you
133 must call QApplication::processEvents() or
134 QEventLoop::processEvents(ExcludeUserInput) to keep the event loop
135 running to ensure that the application doesn't freeze. Do the
136 operation in a loop, call \l setProgress() at intervals, and check
137 for cancellation with wasCanceled(). For example:
138\code
139QProgressDialog progress( "Copying files...", "Abort Copy", numFiles,
140 this, "progress", TRUE );
141for ( int i = 0; i < numFiles; i++ ) {
142 progress.setProgress( i );
143 qApp->processEvents();
144
145 if ( progress.wasCanceled() )
146 break;
147 //... copy one file
148}
149progress.setProgress( numFiles );
150\endcode
151
152 A modeless progress dialog is suitable for operations that take
153 place in the background, where the user is able to interact with the
154 application. Such operations are typically based on QTimer (or
155 QObject::timerEvent()), QSocketNotifier, or QUrlOperator; or performed
156 in a separate thread. A QProgressBar in the status bar of your main window
157 is often an alternative to a modeless progress dialog.
158
159 You need to have an event loop to be running, connect the
160 canceled() signal to a slot that stops the operation, and call \l
161 setProgress() at intervals. For example:
162\code
163Operation::Operation( QObject *parent = 0 )
164 : QObject( parent ), steps( 0 )
165{
166 pd = new QProgressDialog( "Operation in progress.", "Cancel", 100 );
167 connect( pd, SIGNAL(canceled()), this, SLOT(cancel()) );
168 t = new QTimer( this );
169 connect( t, SIGNAL(timeout()), this, SLOT(perform()) );
170 t->start( 0 );
171}
172
173void Operation::perform()
174{
175 pd->setProgress( steps );
176 //... perform one percent of the operation
177 steps++;
178 if ( steps > pd->totalSteps() )
179 t->stop();
180}
181
182void Operation::cancel()
183{
184 t->stop();
185 //... cleanup
186}
187\endcode
188
189
190 In both modes the progress dialog may be customized by
191 replacing the child widgets with custom widgets by using setLabel(),
192 setBar(), and setCancelButton().
193 The functions setLabelText() and setCancelButtonText()
194 set the texts shown.
195
196 <img src=qprogdlg-m.png> <img src=qprogdlg-w.png>
197
198 \sa QDialog QProgressBar
199 \link guibooks.html#fowler GUI Design Handbook: Progress Indicator\endlink
200*/
201
202
203/*!
204 Returns the QLabel currently being displayed above the progress bar.
205 This QLabel is owned by the QProgressDialog.
206
207 \sa setLabel()
208*/
209QLabel *QProgressDialog::label() const
210{
211 return d->label;
212}
213
214/*!
215 Returns the QProgressBar currently being used to display progress.
216 This QProgressBar is owned by the QProgressDialog.
217
218 \sa setBar()
219*/
220QProgressBar *QProgressDialog::bar() const
221{
222 return d->bar;
223}
224
225
226/*!
227 Constructs a progress dialog.
228
229 Default settings:
230 \list
231 \i The label text is empty.
232 \i The cancel button text is (translated) "Cancel".
233 \i The total number of steps is 100.
234 \endlist
235
236 The \a creator argument is the widget to use as the dialog's parent.
237 The \a name, \a modal, and the widget flags, \a f, are
238 passed to the QDialog::QDialog() constructor. If \a modal is FALSE (the
239 default), you must have an event loop proceeding for any redrawing
240 of the dialog to occur. If \a modal is TRUE, the dialog ensures that
241 events are processed when needed.
242
243 \sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(),
244 setTotalSteps()
245*/
246
247QProgressDialog::QProgressDialog( QWidget *creator, const char *name,
248 bool modal, WFlags f )
249 : QDialog( creator, name, modal, f)
250{
251 init( creator, QString::fromLatin1(""), tr("Cancel"), 100 );
252}
253
254/*!
255 Constructs a progress dialog.
256
257 The \a labelText is text used to remind the user what is progressing.
258
259 The \a cancelButtonText is the text to display on the cancel button,
260 or 0 if no cancel button is to be shown.
261
262 The \a totalSteps is the total number of steps in the operation for
263 which this progress dialog shows progress. For example, if the
264 operation is to examine 50 files, this value would be 50. Before
265 examining the first file, call setProgress(0). As each file is
266 processed call setProgress(1), setProgress(2), etc., finally
267 calling setProgress(50) after examining the last file.
268
269 The \a creator argument is the widget to use as the dialog's parent.
270 The \a name, \a modal, and widget flags, \a f, are passed to the
271 QDialog::QDialog() constructor. If \a modal is FALSE (the default),
272 you will must have an event loop proceeding for any redrawing of
273 the dialog to occur. If \a modal is TRUE, the dialog ensures that
274 events are processed when needed.
275
276
277 \sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(),
278 setTotalSteps()
279*/
280
281QProgressDialog::QProgressDialog( const QString &labelText,
282 const QString &cancelButtonText,
283 int totalSteps,
284 QWidget *creator, const char *name,
285 bool modal, WFlags f )
286 : QDialog( creator, name, modal, f)
287{
288 init( creator, labelText, cancelButtonText, totalSteps );
289}
290
291
292/*!
293 Destroys the progress dialog.
294*/
295
296QProgressDialog::~QProgressDialog()
297{
298#ifndef QT_NO_CURSOR
299 if ( d->creator )
300 d->creator->setCursor( d->parentCursor );
301#endif
302 delete d;
303}
304
305void QProgressDialog::init( QWidget *creator,
306 const QString& lbl, const QString& canc,
307 int totstps)
308{
309 d = new QProgressDialogData(this, creator, lbl, totstps);
310 d->autoClose = TRUE;
311 d->autoReset = TRUE;
312 d->forceHide = FALSE;
313 setCancelButtonText( canc );
314 connect( this, SIGNAL(canceled()), this, SIGNAL(cancelled()) );
315 connect( this, SIGNAL(canceled()), this, SLOT(cancel()) );
316 forceTimer = new QTimer( this );
317 connect( forceTimer, SIGNAL(timeout()), this, SLOT(forceShow()) );
318 layout();
319}
320
321/*!
322 \fn void QProgressDialog::canceled()
323
324 This signal is emitted when the cancel button is clicked.
325 It is connected to the cancel() slot by default.
326
327 \sa wasCanceled()
328*/
329
330/*!
331 \fn void QProgressDialog::cancelled()
332
333 \obsolete
334
335 Use canceled() instead.
336*/
337
338
339/*!
340 Sets the label to \a label. The progress dialog resizes to fit. The
341 label becomes owned by the progress dialog and will be deleted when
342 necessary, so do not pass the address of an object on the stack.
343
344 \sa setLabelText()
345*/
346
347void QProgressDialog::setLabel( QLabel *label )
348{
349 delete d->label;
350 d->label = label;
351 if (label) {
352 if ( label->parentWidget() == this ) {
353 label->hide(); // until we resize
354 } else {
355 label->reparent( this, 0, QPoint(0,0), FALSE );
356 }
357 }
358 int w = QMAX( isVisible() ? width() : 0, sizeHint().width() );
359 int h = QMAX( isVisible() ? height() : 0, sizeHint().height() );
360 resize( w, h );
361 if (label)
362 label->show();
363}
364
365
366/*!
367 \property QProgressDialog::labelText
368 \brief the label's text
369
370 The default text is QString::null.
371*/
372
373QString QProgressDialog::labelText() const
374{
375 if ( label() )
376 return label()->text();
377 return QString::null;
378}
379
380void QProgressDialog::setLabelText( const QString &text )
381{
382 if ( label() ) {
383 label()->setText( text );
384 int w = QMAX( isVisible() ? width() : 0, sizeHint().width() );
385 int h = QMAX( isVisible() ? height() : 0, sizeHint().height() );
386 resize( w, h );
387 }
388}
389
390
391/*!
392 Sets the cancel button to the push button, \a cancelButton. The
393 progress dialog takes ownership of this button which will be deleted
394 when necessary, so do not pass the address of an object that is on
395 the stack, i.e. use new() to create the button.
396
397 \sa setCancelButtonText()
398*/
399
400void QProgressDialog::setCancelButton( QPushButton *cancelButton )
401{
402 delete d->cancel;
403 d->cancel = cancelButton;
404 if (cancelButton) {
405 if ( cancelButton->parentWidget() == this ) {
406 cancelButton->hide(); // until we resize
407 } else {
408 cancelButton->reparent( this, 0, QPoint(0,0), FALSE );
409 }
410 connect( d->cancel, SIGNAL(clicked()), this, SIGNAL(canceled()) );
411#ifndef QT_NO_ACCEL
412 QAccel *accel = new QAccel( this );
413 accel->connectItem( accel->insertItem(Key_Escape),
414 d->cancel, SIGNAL(clicked()) );
415#endif
416 }
417 int w = QMAX( isVisible() ? width() : 0, sizeHint().width() );
418 int h = QMAX( isVisible() ? height() : 0, sizeHint().height() );
419 resize( w, h );
420 if (cancelButton)
421 cancelButton->show();
422}
423
424/*!
425 Sets the cancel button's text to \a cancelButtonText.
426 \sa setCancelButton()
427*/
428
429void QProgressDialog::setCancelButtonText( const QString &cancelButtonText )
430{
431 if ( !cancelButtonText.isNull() ) {
432 if ( d->cancel )
433 d->cancel->setText(cancelButtonText);
434 else
435 setCancelButton(new QPushButton(cancelButtonText, this, "cancel"));
436 } else {
437 setCancelButton(0);
438 }
439 int w = QMAX( isVisible() ? width() : 0, sizeHint().width() );
440 int h = QMAX( isVisible() ? height() : 0, sizeHint().height() );
441 resize( w, h );
442}
443
444
445/*!
446 Sets the progress bar widget to \a bar. The progress dialog resizes to
447 fit. The progress dialog takes ownership of the progress \a bar which
448 will be deleted when necessary, so do not use a progress bar
449 allocated on the stack.
450*/
451
452void QProgressDialog::setBar( QProgressBar *bar )
453{
454 if ( progress() > 0 ) {
455#if defined(QT_CHECK_STATE)
456 qWarning( "QProgrssDialog::setBar: Cannot set a new progress bar "
457 "while the old one is active" );
458#endif
459 }
460 delete d->bar;
461 d->bar = bar;
462 int w = QMAX( isVisible() ? width() : 0, sizeHint().width() );
463 int h = QMAX( isVisible() ? height() : 0, sizeHint().height() );
464 resize( w, h );
465}
466
467
468/*!
469 \property QProgressDialog::wasCancelled
470 \brief whether the dialog was canceled
471
472 \obsolete
473
474 Use \l wasCanceled instead.
475*/
476
477/*!
478 \property QProgressDialog::wasCanceled
479 \brief whether the dialog was canceled
480
481 \sa setProgress()
482*/
483
484bool QProgressDialog::wasCancelled() const
485{
486 return d->cancellation_flag;
487}
488
489
490/*!
491 \property QProgressDialog::totalSteps
492 \brief the total number of steps
493
494 The default is 0.
495*/
496
497int QProgressDialog::totalSteps() const
498{
499 if ( d && d->bar )
500 return bar()->totalSteps();
501 return 0;
502}
503
504void QProgressDialog::setTotalSteps( int totalSteps )
505{
506 bar()->setTotalSteps( totalSteps );
507}
508
509
510/*!
511 Resets the progress dialog.
512 The progress dialog becomes hidden if autoClose() is TRUE.
513
514 \sa setAutoClose(), setAutoReset()
515*/
516
517void QProgressDialog::reset()
518{
519#ifndef QT_NO_CURSOR
520 if ( progress() >= 0 ) {
521 if ( d->creator )
522 d->creator->setCursor( d->parentCursor );
523 }
524#endif
525 if ( d->autoClose || d->forceHide )
526 hide();
527 bar()->reset();
528 d->cancellation_flag = FALSE;
529 d->shown_once = FALSE;
530 forceTimer->stop();
531}
532
533/*!
534 Resets the progress dialog. wasCanceled() becomes TRUE until
535 the progress dialog is reset.
536 The progress dialog becomes hidden.
537*/
538
539void QProgressDialog::cancel()
540{
541 d->forceHide = TRUE;
542 reset();
543 d->forceHide = FALSE;
544 d->cancellation_flag = TRUE;
545}
546
547/*!
548 \property QProgressDialog::progress
549 \brief the current amount of progress made.
550
551 For the progress dialog to work as expected, you should initially set
552 this property to 0 and finally set it to
553 QProgressDialog::totalSteps(); you can call setProgress() any number of times
554 in-between.
555
556 \warning If the progress dialog is modal
557 (see QProgressDialog::QProgressDialog()),
558 this function calls QApplication::processEvents(), so take care that
559 this does not cause undesirable re-entrancy in your code. For example,
560 don't use a QProgressDialog inside a paintEvent()!
561
562 \sa totalSteps
563*/
564
565int QProgressDialog::progress() const
566{
567 return bar()->progress();
568}
569
570void QProgressDialog::setProgress( int progress )
571{
572 if ( progress == bar()->progress() ||
573 bar()->progress() == -1 && progress == bar()->totalSteps() )
574 return;
575
576 bar()->setProgress(progress);
577
578 if ( d->shown_once ) {
579 if (testWFlags(WShowModal))
580 qApp->processEvents();
581 } else {
582 if ( progress == 0 ) {
583#ifndef QT_NO_CURSOR
584 if ( d->creator ) {
585 d->parentCursor = d->creator->cursor();
586 d->creator->setCursor( waitCursor );
587 }
588#endif
589 d->starttime.start();
590 forceTimer->start( d->showTime );
591 return;
592 } else {
593 bool need_show;
594 int elapsed = d->starttime.elapsed();
595 if ( elapsed >= d->showTime ) {
596 need_show = TRUE;
597 } else {
598 if ( elapsed > minWaitTime ) {
599 int estimate;
600 if ( (totalSteps() - progress) >= INT_MAX / elapsed )
601 estimate = (totalSteps() - progress) / progress * elapsed;
602 else
603 estimate = elapsed * (totalSteps() - progress) / progress;
604 need_show = estimate >= d->showTime;
605 } else {
606 need_show = FALSE;
607 }
608 }
609 if ( need_show ) {
610 int w = QMAX( isVisible() ? width() : 0, sizeHint().width() );
611 int h = QMAX( isVisible() ? height() : 0, sizeHint().height() );
612 resize( w, h );
613 show();
614 d->shown_once = TRUE;
615 }
616 }
617#ifdef Q_WS_MACX
618 QApplication::flush();
619#endif
620 }
621
622 if ( progress == bar()->totalSteps() && d->autoReset )
623 reset();
624}
625
626/*!
627 \overload
628
629 Sets the current amount of progress to \a progress and the total number of
630 steps to \a totalSteps.
631
632 \sa setTotalSteps()
633*/
634
635void QProgressDialog::setProgress( int progress, int totalSteps )
636{
637 setTotalSteps( totalSteps );
638 setProgress( progress );
639}
640
641/*!
642 Returns a size that fits the contents of the progress dialog.
643 The progress dialog resizes itself as required, so you should not
644 need to call this yourself.
645*/
646
647QSize QProgressDialog::sizeHint() const
648{
649 QSize sh = label()->sizeHint();
650 QSize bh = bar()->sizeHint();
651 int h = margin_tb*2 + bh.height() + sh.height() + spacing;
652 if ( d->cancel )
653 h += d->cancel->sizeHint().height() + spacing;
654 return QSize( QMAX(200, sh.width() + 2*margin_lr), h );
655}
656
657/*!\reimp
658*/
659void QProgressDialog::resizeEvent( QResizeEvent * )
660{
661 layout();
662}
663
664/*!
665 \reimp
666*/
667void QProgressDialog::styleChange(QStyle& s)
668{
669 QDialog::styleChange(s);
670 layout();
671}
672
673void QProgressDialog::layout()
674{
675 int sp = spacing;
676 int mtb = margin_tb;
677 int mlr = QMIN(width()/10, margin_lr);
678 const bool centered =
679 bool(style().styleHint(QStyle::SH_ProgressDialog_CenterCancelButton, this));
680
681 QSize cs = d->cancel ? d->cancel->sizeHint() : QSize(0,0);
682 QSize bh = bar()->sizeHint();
683 int cspc;
684 int lh = 0;
685
686 // Find spacing and sizes that fit. It is important that a progress
687 // dialog can be made very small if the user demands it so.
688 for (int attempt=5; attempt--; ) {
689 cspc = d->cancel ? cs.height() + sp : 0;
690 lh = QMAX(0, height() - mtb - bh.height() - sp - cspc);
691
692 if ( lh < height()/4 ) {
693 // Getting cramped
694 sp /= 2;
695 mtb /= 2;
696 if ( d->cancel ) {
697 cs.setHeight(QMAX(4,cs.height()-sp-2));
698 }
699 bh.setHeight(QMAX(4,bh.height()-sp-1));
700 } else {
701 break;
702 }
703 }
704
705 if ( d->cancel ) {
706 d->cancel->setGeometry(
707 centered ? width()/2 - cs.width()/2 : width() - mlr - cs.width(),
708 height() - mtb - cs.height() + sp,
709 cs.width(), cs.height() );
710 }
711
712 label()->setGeometry( mlr, 0, width()-mlr*2, lh );
713 bar()->setGeometry( mlr, lh+sp, width()-mlr*2, bh.height() );
714}
715
716/*!
717 \property QProgressDialog::minimumDuration
718 \brief the time that must pass before the dialog appears
719
720 If the expected duration of the task is less than the
721 minimumDuration, the dialog will not appear at all. This prevents
722 the dialog popping up for tasks that are quickly over. For tasks
723 that are expected to exceed the minimumDuration, the dialog will
724 pop up after the minimumDuration time or as soon as any progress
725 is set.
726
727 If set to 0, the dialog is always shown as soon as any progress is
728 set. The default is 4000 milliseconds.
729*/
730void QProgressDialog::setMinimumDuration( int ms )
731{
732 d->showTime = ms;
733 if ( bar()->progress() == 0 ) {
734 forceTimer->stop();
735 forceTimer->start( ms );
736 }
737}
738
739int QProgressDialog::minimumDuration() const
740{
741 return d->showTime;
742}
743
744
745/*!
746 \reimp
747*/
748
749void QProgressDialog::closeEvent( QCloseEvent *e )
750{
751 emit canceled();
752 QDialog::closeEvent( e );
753}
754
755/*!
756 \property QProgressDialog::autoReset
757 \brief whether the progress dialog calls reset() as soon as progress() equals totalSteps()
758
759 The default is TRUE.
760
761 \sa setAutoClose()
762*/
763
764void QProgressDialog::setAutoReset( bool b )
765{
766 d->autoReset = b;
767}
768
769bool QProgressDialog::autoReset() const
770{
771 return d->autoReset;
772}
773
774/*!
775 \property QProgressDialog::autoClose
776 \brief whether the dialog gets hidden by reset()
777
778 The default is TRUE.
779
780 \sa setAutoReset()
781*/
782
783void QProgressDialog::setAutoClose( bool b )
784{
785 d->autoClose = b;
786}
787
788bool QProgressDialog::autoClose() const
789{
790 return d->autoClose;
791}
792
793/*!
794 \reimp
795*/
796
797void QProgressDialog::showEvent( QShowEvent *e )
798{
799 QDialog::showEvent( e );
800 int w = QMAX( isVisible() ? width() : 0, sizeHint().width() );
801 int h = QMAX( isVisible() ? height() : 0, sizeHint().height() );
802 resize( w, h );
803 forceTimer->stop();
804}
805
806/*!
807 Shows the dialog if it is still hidden after the algorithm has been started
808 and minimumDuration milliseconds have passed.
809
810 \sa setMinimumDuration()
811*/
812
813void QProgressDialog::forceShow()
814{
815 if ( d->shown_once || d->cancellation_flag )
816 return;
817
818 show();
819 d->shown_once = TRUE;
820}
821
822
823#endif
Note: See TracBrowser for help on using the repository browser.